//****************************************************************************************************** // SecurityProviderCache.cs - Gbtc // // Copyright © 2012, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the Eclipse Public License -v 1.0 (the "License"); you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://www.opensource.org/licenses/eclipse-1.0.php // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 06/25/2010 - Pinal C. Patel // Generated original version of source code. // 01/27/2011 - Pinal C. Patel // Updated SetupPrincipal() to call AppDomain.SetThreadPrincipal() to set the thread principal // in addition to settings the current thread principal so the thread principal is set correctly // inside WPF applications. // 12/20/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Collections.Generic; using System.Security.Principal; using System.Threading; using System.Timers; using System.Web; using Timer = System.Timers.Timer; namespace GSF.Security { /// /// A helper class that manages the caching of s. /// public static class SecurityProviderCache { #region [ Members ] // Nested Types /// /// A class that facilitates the caching of . /// private class CacheContext { private readonly ISecurityProvider m_provider; private DateTime m_lastAccessed; /// /// Initializes a new instance of the class. /// public CacheContext(ISecurityProvider provider) { m_provider = provider; m_lastAccessed = DateTime.Now; } /// /// Gets the managed by this . /// public ISecurityProvider Provider { get { m_lastAccessed = DateTime.Now; return m_provider; } } /// /// Gets the of when the was last accessed. /// public DateTime LastAccessed { get { return m_lastAccessed; } } } // Constants /// /// Number of minutes upto which s are to be cached. /// private const int CachingTimeout = 20; #endregion #region [ Static ] // Static Fields private static bool s_threadPolicySet; private static readonly IDictionary s_cache; private static readonly Timer s_cacheMonitorTimer; // Static Constructor static SecurityProviderCache() { // Initialize static variables. s_cache = new Dictionary(StringComparer.CurrentCultureIgnoreCase); s_cacheMonitorTimer = new Timer(60000); s_cacheMonitorTimer.Elapsed += CacheMonitorTimer_Elapsed; s_cacheMonitorTimer.Start(); } // Static Properties /// /// Gets or sets the of the current user. /// public static ISecurityProvider CurrentProvider { get { // Logic behind caching of the provider: // - A provider is cached to session state data if the runtime is ASP.NET and if the session state // data is accessible. This would essentially mean that we're dealing with web sites or web services // that are either SOAP ASMX services or WCF services hosted in ASP.NET compatibility mode. // - A provider is cached to in-process static memory if we don't have access to session state data. // This would essentially mean that we're either dealing with windows based application or WCF // service hosted inside ASP.NET runtime without compatibility mode enabled. SecurityPrincipal principal = Thread.CurrentPrincipal as SecurityPrincipal; if (principal != null) { // The provider we're looking for is available to us via the current thread principal. This means // that the current thread principal has already been set by a call to Current property setter. return ((SecurityIdentity)principal.Identity).Provider; } else { // Since the provider is not available to us through the current thread principal, we check to see // if it is available to us via one of the two caching mechanisms. if (HttpContext.Current != null && HttpContext.Current.Session != null) { // Check session state. ISecurityProvider provider = HttpContext.Current.Session[typeof(ISecurityProvider).Name] as ISecurityProvider; if (provider == null) return null; else return SetupPrincipal(provider, false); } else { // Check in-process memory. CacheContext cache; lock (s_cache) { s_cache.TryGetValue(Thread.CurrentPrincipal.Identity.Name, out cache); } if (cache == null) return null; else return SetupPrincipal(cache.Provider, false); } } } set { if (value != null) { // Login - Setup security principal. value.Initialize(); SetupPrincipal(value, false); if (HttpContext.Current != null && HttpContext.Current.Session != null) // Cache provider to session state. HttpContext.Current.Session[typeof(ISecurityProvider).Name] = value; else if (!string.IsNullOrEmpty(value.UserData.LoginID)) // Cache provider to in-process memory. lock (s_cache) { s_cache[value.UserData.LoginID] = new CacheContext(value); } } else { // Logout - Restore original principal. SecurityPrincipal principal = Thread.CurrentPrincipal as SecurityPrincipal; if (principal == null) return; SecurityIdentity identity = (SecurityIdentity)principal.Identity; SetupPrincipal(identity.Provider, true); if (HttpContext.Current != null && HttpContext.Current.Session != null) // Remove previously cached provider from session state. HttpContext.Current.Session[typeof(ISecurityProvider).Name] = null; else if (s_cache.ContainsKey(identity.Provider.UserData.LoginID)) // Remove previously cached provider from in-process memory. lock (s_cache) { s_cache.Remove(identity.Provider.UserData.LoginID); } } } } // Static Methods /// /// Attempts to get cached for the given username. /// /// Name of the user. /// Security provider to return. /// True if provider is cached; false otherwise. public static bool TryGetCachedProvider(string username, out ISecurityProvider provider) { CacheContext cacheContext; bool result = s_cache.TryGetValue(username, out cacheContext); provider = result ? cacheContext.Provider : null; return result; } private static ISecurityProvider SetupPrincipal(ISecurityProvider provider, bool restore) { // Initialize the principal object. IPrincipal principal; if (restore) // Set principal to anonymous WindowsPrincipal. principal = new WindowsPrincipal(WindowsIdentity.GetAnonymous()); else // Set principal to SecurityPrincipal. principal = new SecurityPrincipal(new SecurityIdentity(provider)); // Setup the current thread principal. Thread.CurrentPrincipal = principal; if (!s_threadPolicySet) { AppDomain.CurrentDomain.SetThreadPrincipal(Thread.CurrentPrincipal); s_threadPolicySet = true; } // Setup ASP.NET remote user principal. if (HttpContext.Current != null) HttpContext.Current.User = Thread.CurrentPrincipal; return provider; } private static void CacheMonitorTimer_Elapsed(object sender, ElapsedEventArgs e) { lock (s_cache) { List cacheKeys = new List(s_cache.Keys); foreach (string cacheKey in cacheKeys) { if (DateTime.Now.Subtract(s_cache[cacheKey].LastAccessed).TotalMinutes > CachingTimeout) s_cache.Remove(cacheKey); } } } #endregion } }