# Monday, 21 November 2011

If you ever need to redirect to a custom page in ASP.NET MVC when a user is either not authenticated or not authorized here is how you do it.

  1. Create a custom attribute that inherits from AuthorizeAttribute.
  2. Override the OnAuthorization method.
    • call the base OnAuthorization
    • Handle user not being authenticated
    • Handle user not being authorized
  3. Use the newly created attribute in your controller in place of the ASP.NET Authorize attribute

 

The Custom Attribute
  1. public class CustomAuthorizeAttribute : AuthorizeAttribute {
  2.  
  3.     public override void OnAuthorization(AuthorizationContext filterContext) {
  4.         base.OnAuthorization(filterContext);
  5.         if (!filterContext.HttpContext.User.Identity.IsAuthenticated) {
  6.             filterContext.Result = new RedirectResult("~/Account/Logon");
  7.             return;
  8.         }
  9.  
  10.         if (filterContext.Result is HttpUnauthorizedResult) {
  11.             filterContext.Result = new RedirectResult("~/Account/AccessDenied");
  12.             return;
  13.         }
  14.     }
  15. }
Example Controller Usage
  1. [CustomAuthorize(Roles = ("Admin,Manager"))]
  2. public ActionResult Index() {
  3.     return View("Index");
  4. }
posted on Monday, 21 November 2011 09:46:18 (Central Standard Time, UTC-06:00)  #    Comments [9]
# Monday, 16 August 2010

Note: This expands on the code created in my previous post: Requiring Log-On in a WPF MVVM Composite Application

* Download Complete Source Code

Continuing on my WPF MVVM Adventures...  Well, I am still waiting on the business to finalize the requirements so I am going to take a shot at coding it to what I think the final specs will be.  Although this is a very common situation, when you take this slippery slope you need to be careful that you don’t end up creating throw away code.   Make sure you:

  1. Program to Interfaces. (FYI – you should be doing this anyway!)
  2. Add Abstraction layers that will allow you to swap out implementation. (See step 1)
  3. Keep all the possible solutions to the problem in mind when designing/coding.  In my case I am 99% certain the business will want security down to the composite module level, but I am still going to leave paths open that will allow me to implement field level security without too much re-work.

The first thing I need is to define by security roles.  Since the actual roles are TBD I am just going to create a simple Enumeration.

Enumerations.cs
  1. namespace CompositeAppPoc.Infrastructure.Enumerations {
  2.  
  3.     public enum AuthorizationLevel {
  4.         ReadOnly,
  5.         ReadWrite,
  6.         Admin
  7.     }
  8. }

In thinking about what the Authentication method of my yet to be created service will return I decided it was going to need to return multiple things.

  1. Boolean representing whether or not the credentials where able to be authenticated.
  2. Level of authorization granted to credentials.
  3. List of modules that credentials are able to view.

Knowing I was going to need to return multiple values I created a data transfer object (DTO) to handle it.

AuthenticationContext.cs
  1. using System.Collections.Generic;
  2. using CompositeAppPoc.Infrastructure.Enumerations;
  3.  
  4. namespace CompositeAppPoc.Infrastructure.DataTransferObjects {
  5.  
  6.     public class AuthenticationContext {
  7.  
  8.         public bool IsAuthorized { get; set; }
  9.  
  10.         public AuthorizationLevel AuthLevel { get; set; }
  11.  
  12.         public IList<string> AllowedModules { get; set; }
  13.  
  14.     }
  15. }

Now I can create my service.  This is going to reside in my Infrastructure project and will allow me to push(abstract) the implementation of the actual authentication farther down the stack.  Sticking to what I recommended at the beginning of this post I am going to create a simple interface for the service.  The interface will declare one signature, Authenticate, that accepts a username and password and returns an AuthenticationContext.

IAuthenticationService
  1. using CompositeAppPoc.Infrastructure.DataTransferObjects;
  2.  
  3. namespace CompositeAppPoc.Infrastructure.Interfaces {
  4.  
  5.     public interface IAuthenticationService {
  6.         AuthenticationContext Authenticate(string userName, string password);
  7.     }
  8. }

Next we need to code the actual implementation of IAuthenticationService.  At this time the implementation of Authenticate will need to perform the actual authentication, which will set the IsAuthorized flag as well as the AuthLevel, and determine what modules are viewable for the AuthLevel.  Because I still have not defined the details of these process I am going to “stub” them out with a trivial implementation.

AuthenticationService.cs
  1. using System.Collections.Generic;
  2. using CompositeAppPoc.Infrastructure.Constants;
  3. using CompositeAppPoc.Infrastructure.DataTransferObjects;
  4. using CompositeAppPoc.Infrastructure.Enumerations;
  5. using CompositeAppPoc.Infrastructure.Interfaces;
  6.  
  7. namespace CompositeAppPoc.Infrastructure.Services {
  8.  
  9.     public class AuthenticationService : IAuthenticationService {
  10.  
  11.         private readonly AuthenticationContext _authenticationContext = new AuthenticationContext();
  12.  
  13.         public AuthenticationContext Authenticate(string userName, string password) {
  14.             Authorize(userName, password);
  15.             if (_authenticationContext.IsAuthorized) {
  16.                 GetApprovedModules();
  17.             }
  18.             return _authenticationContext;
  19.         }
  20.  
  21.         private void Authorize(string userName, string password) {
  22.             if (userName.Equals("admin") && password.Equals("pass")) {
  23.                 _authenticationContext.IsAuthorized = true;
  24.                 _authenticationContext.AuthLevel = AuthorizationLevel.Admin;
  25.             }
  26.             else if (userName.Equals("readwrite") && password.Equals("pass")) {
  27.                 _authenticationContext.IsAuthorized = true;
  28.                 _authenticationContext.AuthLevel = AuthorizationLevel.ReadWrite;
  29.             }
  30.             else if (userName.Equals("readonly") && password.Equals("pass")) {
  31.                 _authenticationContext.IsAuthorized = true;
  32.                 _authenticationContext.AuthLevel = AuthorizationLevel.ReadOnly;
  33.             }
  34.         }
  35.  
  36.         private void GetApprovedModules() {
  37.             IList<string> modules = new List<string>();
  38.             switch (_authenticationContext.AuthLevel) {
  39.                 case AuthorizationLevel.Admin:
  40.                     modules.Add(EmployeeConstants.EmployeeEditModuleName);
  41.                     modules.Add(EmployeeConstants.EmployeeCreateModuleName);
  42.                     break;
  43.                 case AuthorizationLevel.ReadWrite:
  44.                     modules.Add(EmployeeConstants.EmployeeCreateModuleName);
  45.                     break;
  46.             }
  47.             _authenticationContext.AllowedModules = modules;
  48.         }
  49.     }
  50. }

Now we need to hop over the Shell project and start making adding in the hooks for our module level security.

This first I am going to do is change how the modules are loaded.  Initially I used the ConfigurationModuleCatalog which loads the modules from a configuration file.  I decided against this approach because I wanted to avoid configuration changes when a module was added or removed.  Luckily Prism provides another option called the DirectoryModuleCatalog.  This allows you to supply a directory path that contains the dll’s for your modules.  So, all that needs to be done in order to add a new module is to drop its dll’s in that directory.  This change needs to implemented in the bootstrapper and while we are in there we need to tell our IOC container how to wire up our AuthenticationService.

Bootstrapper.cs
  1. using System.Windows;
  2. using CompositeAppPoc.Infrastructure.Interfaces;
  3. using CompositeAppPoc.Infrastructure.Services;
  4. using Microsoft.Practices.Composite.Modularity;
  5. using Microsoft.Practices.Composite.UnityExtensions;
  6. using Microsoft.Practices.Unity;
  7.  
  8. namespace CompositeAppPoc.Shell {
  9.  
  10.     public class Bootstrapper : UnityBootstrapper {
  11.  
  12.         protected override DependencyObject CreateShell() {
  13.             Shell shell = new Shell();
  14.             shell.Show();
  15.             return shell;
  16.         }
  17.  
  18.         protected override IModuleCatalog GetModuleCatalog() {
  19.             return new DirectoryModuleCatalog() {ModulePath=Infrastructure.Constants.GeneralConstants.ModulePath};
  20.         }
  21.  
  22.         protected override void ConfigureContainer() {
  23.             base.ConfigureContainer();
  24.             Container.RegisterType<IAuthenticationService, AuthenticationService>(new ContainerControlledLifetimeManager());
  25.             Container.RegisterType<IEmployeeDataService, EmployeeDataService>(new ContainerControlledLifetimeManager());
  26.             Container.RegisterType<IModuleManager, ModuleManager>(new ContainerControlledLifetimeManager());
  27.         }
  28.     }
  29. }

So now that we did that we can more easily add new modules but it’s still all or nothing.  We need to tell Prism to not load certain modules until we tell it to…load on demand.  This is accomplished by simply by decorating the modules you want to load on demand with the Module attribute and setting the ModuleName and OnDemand properties.  As you can guess we are going to set the OnDemand property to “True”.  This tells Prism, when it is processing the modules in the directory we tell it, not to load the module just yet.  Prism notes the name of the module and waits for us to tell it when to load it.  We will do this on the EmployeeEditModule, and EmployeeCreateModule which will both require authorization but we will not do this to the EmployeeListModule because that module is viewable by all.

EmployeeEditModule.cs
  1. [Module(ModuleName=EmployeeConstants.EmployeeEditModuleName, OnDemand=true)]
  2. public class EmployeeEditModule : IModule {

EmployeeCreateModule.cs
  1. [Module(ModuleName=EmployeeConstants.EmployeeCreateModuleName, OnDemand=true)]
  2. public class EmployeeCreateModule : IModule {

Now when you run the the shell you won’t be able to edit or create a new employee because those modules are not loaded which means that nothing is registered to the associated commands.

We already have the list of allowed secured modules being returned from the authentication service so we just need to write a method to process the list and load the modules.  This method will be in the Bootstrapper and it will load the allowed secured modules using the Module Manager.

LoadSecuredModules
  1. public void LoadSecuredModules(IList<string> allowedModules) {
  2.     var moduleManager = Container.Resolve<IModuleManager>();
  3.     foreach (string module in allowedModules) {
  4.         moduleManager.LoadModule(module);
  5.     }
  6. }

Lastly we need to call LoadSecuredModules.  We will do this as the last step in the App.xaml’s StartUp method.

StartUp
  1. private static void StartUp(IList<string> allowedModules) {
  2.     Current.MainWindow = null;
  3.     Current.ShutdownMode = ShutdownMode.OnMainWindowClose;
  4.     var bs = new Bootstrapper();
  5.     bs.Run();
  6.     bs.LoadSecuredModules(allowedModules);
  7. }

Now when we log in with a read-only id (readonly,pass) the edit and create new buttons are not enabled because the modules are not loaded.

image

* Download Complete Source Code

posted on Monday, 16 August 2010 17:40:00 (Central Daylight Time, UTC-05:00)  #    Comments [39]