//****************************************************************************************************** // SecurityModule.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: // ---------------------------------------------------------------------------------------------------- // 03/31/2010 - Pinal C. Patel // Generated original version of source code. // 05/27/2010 - Pinal C. Patel // Added usage example to code comments. // 06/30/2010 - Pinal C. Patel // Modified redirection logic to support security of static resources (*.txt, *.pdf, *.exe). // 07/01/2010 - Pinal C. Patel // Modified redirection logic to allow for custom redirection using customErrors settings. // 08/11/2010 - Pinal C. Patel // Made key methods virtual for extensibility. // 10/14/2010 - Pinal C. Patel // Modified GetResourceName() to return path and query for consistency with SecurityPolicy. // 01/06/2011 - Pinal C. Patel // Implemented logic to use RestrictAccessAttribute for access control on the requested resource. // 06/09/2011 - Pinal C. Patel // Added null reference check in IsAccessRestricted() method. // 07/29/2011 - Pinal C. Patel // Made the referral URL passed to SecurityPortal.aspx page relative so redirection work correctly // when reverse proxy is involved where requested URL that the web server sees is different than // what the user requested. // 08/18/2011 - Pinal C. Patel // Made Redirect() overridable by deriving classes to control the redirection behavior. // 08/19/2011 - Pinal C. Patel // Changed the type input parameter for Redirect() from int to HttpStatusCode. // 12/20/2012 - Starlynn Danyelle Gilliam // Modified Header. // //****************************************************************************************************** using System; using System.Net; using System.Web; using System.Web.Configuration; using System.Web.Hosting; using System.Web.SessionState; using GSF.Configuration; using GSF.Security; using GSF.Web.Hosting; namespace GSF.Web { #region [ Enumerations ] #endregion /// /// Represents an HTTP module that can be used to enable site-wide role-based security. /// /// /// Required config file entries: /// /// /// /// ///
/// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// /// ]]> /// /// /// public class SecurityModule : IHttpModule { #region [ Members ] // Nested Types /// /// A handler used to force the SessionStateModule to load session state. /// private class SessionEnabledHandler : IHttpHandler, IRequiresSessionState { public readonly IHttpHandler OriginalHandler; /// /// Initializes a new instance of the class. /// /// The original handler object. public SessionEnabledHandler(IHttpHandler originalHandler) { OriginalHandler = originalHandler; } /// /// This method will never get called. /// public void ProcessRequest(HttpContext context) { throw new NotSupportedException(); } /// /// Returns false since class has a member. /// public bool IsReusable { get { return false; } } } // Fields private HttpApplication m_application; #endregion #region [ Properties ] /// /// Gets the current instance. /// protected HttpApplication Application { get { return m_application; } } #endregion #region [ Methods ] /// /// Initializes the . /// /// An object. public void Init(HttpApplication context) { m_application = context; m_application.PostMapRequestHandler += Application_PostMapRequestHandler; m_application.PostAcquireRequestState += Application_PostAcquireRequestState; m_application.PreRequestHandlerExecute += Application_PreRequestHandlerExecute; if (!(HostingEnvironment.VirtualPathProvider is EmbeddedResourcePathProvider)) HostingEnvironment.RegisterVirtualPathProvider(new EmbeddedResourcePathProvider()); } /// /// Releases the resources used by . /// public void Dispose() { m_application.PostMapRequestHandler -= Application_PostMapRequestHandler; m_application.PostAcquireRequestState -= Application_PostAcquireRequestState; m_application.PreRequestHandlerExecute -= Application_PreRequestHandlerExecute; } /// /// Redirects the client browser based on the specified /// /// to be used for the redirect. protected virtual void Redirect(HttpStatusCode httpStatusCode) { int statusCode = (int)httpStatusCode; if (m_application.Context.IsCustomErrorEnabled) { // Defer redirect to customErrors settings in the config file to allow for custom error pages. ConfigurationFile configFile = ConfigurationFile.Current; CustomErrorsSection customErrors = configFile.Configuration.GetSection("system.web/customErrors") as CustomErrorsSection; if (customErrors != null && customErrors.Errors[statusCode.ToString()] != null) { // Set status code for the response. m_application.Context.Response.StatusCode = statusCode; // Throw exception for ASP.NET pipeline to takeover processing. throw new HttpException(statusCode, string.Format("Security exception (HTTP status code: {0})", statusCode)); } } // Abruptly ending the processing caused by a redirect does not work well when processing static content. string redirectUrl = string.Format("~/SecurityPortal.aspx?s={0}&r={1}", statusCode, HttpUtility.UrlEncode(VirtualPathUtility.ToAppRelative(m_application.Request.Url.PathAndQuery))); if (m_application.Context.Handler is DefaultHttpHandler) // Accessed resource is static. m_application.Context.Response.Redirect(redirectUrl, false); else // Accessed resource is dynamic. m_application.Context.Response.Redirect(redirectUrl, true); } /// /// Gets the name of resource being accessed. /// /// .. property value. protected virtual string GetResourceName() { return m_application.Request.Url.PathAndQuery; } /// /// Determines if access to the requested resource is restricted by . /// /// true if access to the requested resource is restricted, otherwise false. protected virtual bool IsAccessRestricted() { // Check for the request handler. IHttpHandler handler = m_application.Context.Handler; if (handler == null) return false; // Evaluate access restriction if defined. object[] attributes = handler.GetType().GetCustomAttributes(typeof(RestrictAccessAttribute), true); if (attributes.Length > 0) return !((RestrictAccessAttribute)attributes[0]).CheckAccess(); else return false; } private void Application_PostMapRequestHandler(object sender, EventArgs e) { if (!SecurityProviderUtility.IsResourceSecurable(GetResourceName())) return; if (m_application.Context.Handler is IReadOnlySessionState || m_application.Context.Handler is IRequiresSessionState) // no need to replace the current handler return; // swap the current handler m_application.Context.Handler = new SessionEnabledHandler(m_application.Context.Handler); } private void Application_PostAcquireRequestState(object sender, EventArgs e) { if (!SecurityProviderUtility.IsResourceSecurable(GetResourceName())) return; SessionEnabledHandler handler = HttpContext.Current.Handler as SessionEnabledHandler; if (handler != null) // set the original handler back HttpContext.Current.Handler = handler.OriginalHandler; } private void Application_PreRequestHandlerExecute(object sender, EventArgs e) { string resource = GetResourceName(); if (!SecurityProviderUtility.IsResourceSecurable(resource)) return; if (SecurityProviderCache.CurrentProvider == null) SecurityProviderCache.CurrentProvider = SecurityProviderUtility.CreateProvider(string.Empty); if (!m_application.User.Identity.IsAuthenticated) // Failed to authenticate user. Redirect(HttpStatusCode.Unauthorized); if (IsAccessRestricted() || !SecurityProviderUtility.IsResourceAccessible(resource)) // User does not have access to the resource. Redirect(HttpStatusCode.Forbidden); } #endregion } }