Recently I needed to provide a custom security enforcement for an ASP.net MVC application and confirm it was working by unit testing the application without launching the browser as in an integration test. The reason for extending the AuthorizeAttribute class is that we might decide to store user credential information in a variety of differently data sources such as Active Directory, a database, an encrypted text file, etc…Or we might add custom logic to authorize a user. For the purpose of this article, I am going to simplify the authentication/authorization requirements by defining this scope:
- Application is an intranet application with windows authentication.
- User credential is to be checked against Active Directory.
- Authentication information is stored in web.config of application as a comma delimited string with Active Directive groups and windows user login names such as:
<addkey=”Administrators"value=”ABC\\WebGroup1, ABC\\WebGroup1, ABC\\JaneDoe, ABC\\MaryFisher/>.If user belongs to any of the above Active Directory Groups or widows log in names access is granted, otherwise access denied.
OK, now we have set up our premises, let’s dive straight into the code for the subclass of AuthorizeAttribute:
- namespace SecurityDemo.Classes
- {
- [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]
- public class CustomAuthorizeAttribute: AuthorizeAttribute
- {
- public override voidOnAuthorization(AuthorizationContextfilterContext)
- {
- if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
- //the user is not allowed to execute the Action. An Unauthorized result is raised.
- filterContext.Result = newHttpUnauthorizedResult();
- var roles = GetAuthorizedRoles();
- stringwindowLoginName = filterContext.HttpContext.User.Identity.Name;
- //windowLoginName and ADGroup is expected to have this format "ABC\\XYZ"
- stringdomainName = windowLoginName.Contains(@ "\") ?windowLoginName.Substring(0, windowLoginName.IndexOf(@"\
- ", System.StringComparison.OrdinalIgnoreCase)) : windowLoginName;
- windowLoginName = windowLoginName.Contains(@ "\") ? windowLoginName.Substring(windowLoginName.LastIndexOf(@ "\", System.StringComparison.OrdinalIgnoreCase) + 1): windowLoginName; boolisValidUser = false;
- if (roles.Any(role => ADClass.IsUserInADGroup(windowLoginName, role.Substring(role.LastIndexOf(@ "\", System.StringComparison.OrdinalIgnoreCase) + 1), domainName))) //if window login belongs to AD group from config
- {
- isValidUser = true;
- }
- elseif (roles.Any(role => windowLoginName.ToLower().Equals(role.Substring(role.LastIndexOf(@ "\", System.StringComparison.OrdinalIgnoreCase) + 1).ToLower()))) //if window login belongs to a user from config
- {
- isValidUser = true;
- }
- if (isValidUser)
- {
- return;
- }
- else
- {
- HandleUnauthorizedRequest(filterContext);
- }
- }
- protected override void HandleUnauthorizedRequest(AuthorizationContextfilterContext)
- {
- filterContext.Result = newViewResult
- {
- ViewName = "~/Views/Shared/UnAuthorized.cshtml"
- };
- }
- //get list of authorized Active Directory groups and windows users from
- // web.config
- privateIEnumerable < string > GetAuthorizedRoles()
- {
- var appSettings = ConfigurationManager.AppSettings[this.Roles];
- if (string.IsNullOrEmpty(appSettings))
- {
- return new[]
- {
- ""
- };
- }
- IEnumerable < string > rolesEnumerable = appSettings.Split(',').Select(s => s.Trim());
- return rolesEnumerable;
- }
- }
- }
In the sublassCustomAuthorizeAttribute above we override the OnAuthorization(Authorization Context filterContext) method and provide the logic to identify the windows login user, check the person against the list of authorized Active Directory groups and Windows users from web.config. We also override against the HandleUnauthorizedRequest(AuthorizationContextfilterContext) method to return a view for access denied. Of course, as mentioned, the authorization logic can be made as flexible and complex as possible according to specific business needs.
To use the extended attribute in a controller, we just apply to attribute to a method or class as in the below code snippet:
- public class ProductController: Controller
- {
- [CustomAuthorize(Roles = SystemRole.Administrators)]
- public ActionResultIndex()
- {
- return View("Index");
- }
- [CustomAuthorize(Roles = SystemRole.Administrators)]
- public ActionResultDetails(int Id)
- {
- return View("Details");
- }
- }
- // a helper class to define roles
- public class SystemRole
- {
- public const string Administrators = "Administrators";
- public cons tstring Sales = "Sales";
- }
Arul RPosted Jan 27, 2016, 8:39 AM
Nice
Raja TPosted Jan 27, 2016, 8:05 AM
Nice, Thanks for sharing
Ankit BansalPosted Jan 27, 2016, 6:24 AM
Nice share..thanks..
Mohammed IbrahimPosted Jan 27, 2016, 3:22 AM
nice
Debasis SahaPosted Jan 26, 2016, 9:11 AM
Good One..
Mithun PattankarPosted Jan 26, 2016, 8:51 AM
Well article is very good but it's not part of ASP.NET Core. This is existing ASP.NET MVC 5
Ehsan SajjadPosted Jan 26, 2016, 7:10 AM
Good one
Mohammed IbrahimPosted Jan 26, 2016, 6:49 AM
nice
Tapan DubeyPosted Jan 26, 2016, 4:58 AM
Nice Article thanks for sharing...
Shashangka ShekharPosted Jan 26, 2016, 4:56 AM
Nice
Muhammad Aqib ShehzadPosted Jan 26, 2016, 4:39 AM
fabulous article
Bhavik PatelPosted Jan 26, 2016, 3:36 AM
good article... well explained
Jaco ZwartsPosted Jan 26, 2016, 2:28 AM
Nice Article