//****************************************************************************************************** // AuthorizeControllerRoleAttribute.cs - Gbtc // // Copyright © 2016, 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 MIT License (MIT), the "License"; you may not use this // file except in compliance with the License. You may obtain a copy of the License at: // // http://opensource.org/licenses/MIT // // 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: // ---------------------------------------------------------------------------------------------------- // 02/23/2016 - J. Ritchie Carroll // Generated original version of source code. // //****************************************************************************************************** using System; using System.Linq; using System.Security; using System.Threading; using System.Web.Mvc; using GSF.Collections; using GSF.Security; namespace GSF.Web.Security { /// /// Defines an MVC filter attribute to handle the GSF role based security model. /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false)] public class AuthorizeControllerRoleAttribute : FilterAttribute, IAuthorizationFilter, IExceptionFilter { #region [ Members ] // Fields /// /// Gets allowed roles as a string array. /// public readonly string[] AllowedRoles; #endregion #region [ Constructors ] /// /// Creates a new MVC based authorization attribute. /// public AuthorizeControllerRoleAttribute() { AllowedRoles = new string[0]; } /// /// Creates a new MVC based authorization attribute for specified roles. /// public AuthorizeControllerRoleAttribute(string allowedRoles) { AllowedRoles = allowedRoles?.Split(',').Select(role => role.Trim()). Where(role => !string.IsNullOrEmpty(role)).ToArray() ?? new string[0]; } #endregion #region [ Properties ] /// /// Gets or sets settings category used to lookup security connection for user data context. /// public string SecuritySettingsCategory { get; set; } = "securityProvider"; /// /// Gets or sets view name to show for security exceptions. /// public string SecurityExceptionViewName { get; set; } = "SecurityError"; #endregion #region [ Methods ] /// /// Called when authorization is required. /// /// The filter context. public void OnAuthorization(AuthorizationContext filterContext) { SecurityPrincipal securityPrincipal = filterContext.HttpContext.User as SecurityPrincipal; if ((object)securityPrincipal == null || (object)securityPrincipal.Identity == null) { filterContext.Result = new HttpUnauthorizedResult($"Authentication failed for user \"{filterContext.HttpContext.User?.Identity.Name}\"."); filterContext.HttpContext.User = null; return; } // Get current user name string username = securityPrincipal.Identity.Name; // Verify that the current thread principal has been authenticated. if (!securityPrincipal.Identity.IsAuthenticated) { filterContext.Result = new HttpUnauthorizedResult($"User \"{username}\" is not authenticated."); filterContext.HttpContext.User = null; } else if (AllowedRoles.Length > 0 && !AllowedRoles.Any(role => securityPrincipal.IsInRole(role))) { filterContext.Result = new HttpUnauthorizedResult($"Access is denied for user \"{username}\": minimum required roles = {AllowedRoles.ToDelimitedString(", ")}."); filterContext.HttpContext.User = null; } else { ThreadPool.QueueUserWorkItem(start => AuthorizationCache.CacheAuthorization(username, SecuritySettingsCategory)); } } /// /// Called when an exception occurs. /// /// The filter context. public void OnException(ExceptionContext filterContext) { if (filterContext.Exception is SecurityException) { filterContext.Result = new ViewResult { ViewName = SecurityExceptionViewName, ViewData = new ViewDataDictionary { ["Exception"] = filterContext.Exception } }; filterContext.ExceptionHandled = true; } } #endregion } }