Using ASP.NET Custom Authorization Attribute to Hide Page Elements
By:
Siva Katir
- 5/30/2014
<p>The built in account tools for ASP.NET are pretty powerful out of the box. But one of the few things you see is how to use Roles to show and hide on page content. Generally examples about the Identity Provider focus on just using the Authorization Attribute for security. But what if you need to inject a button into a page, or an administration bar, based on a user's role? </p> <p>Here at POSGuys.com this was a very early problem for us that we solved with a custom <a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.authorizeattribute(v=vs.118).aspx">ASP.NET Authorize Attribute</a>.</p> <p><img src="https://posguys.blob.core.windows.net/content/blog/15/admin-bar.PNG" alt="POSGuys.com Administration Bar" /></p> <p>One of the ways we use this is to display a secondary navigation bar to our administrators. This gives us quick access to error reports, user management, product editors, and everything else you need to efficiently run a website with 50,000+ products.</p> <p>Instead of using the standard Roles methodology, that is a user is assigned to multiple roles, we assign a user to one role. This works better for us but doesn't always make sense. Because a user has a single role they inherit not just their role but all roles below them.</p> <h3>Setting Up</h3> <p>First we need to create the custom Attribute</p> <pre><code>public class AdminAuthorizeAttribute : AuthorizeAttribute { // POSGuys.com Roles private static readonly List<string> _roles = new List<string>(new[] { "User", "Staff", "Sales", "Editor", "Manager", "Admin" }); // The two methods we need to override protected override bool AuthorizeCore(HttpContextBase httpContext) { return base.AuthorizeCore(httpContext); } protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { base.HandleUnauthorizedRequest(filterContext); return; } } </code></pre> <p>Because we're showing additional data on the page we don't want to bounce un-authorized users to the login page, so we have an additional field in our attribute.</p> <pre><code>private bool _redirect = true; // Uses a getter and setter because default must be true. public bool Redirect { get { return _redirect; } set { _redirect = value; } } </code></pre> <p>Now to override the <code>AuthorizeCore</code> method:</p> <pre><code>protected override bool AuthorizeCore(HttpContextBase httpContext) { // short circuit if (httpContext.User.IsInRole(Roles)) { return true; } // If the roles array doesn't contain the roll skip if (_roles.Contains(Roles)) { int role = _roles.IndexOf(Roles); int uRole = -1; // iterate through roles to get the index foreach (string r in _roles) { // This is not a good method for production because // the default provider .IsInRole() method will // trigger a db query on each pass. if (httpContext.User.IsInRole(r)) { uRole = _roles.IndexOf(r); break; } } // if the user's role index is the same or higher authorize if (uRole >= role) { return true; } } // default return false return false; } </code></pre> <p>So now if someone isn't logged in their request will be sent to <code>HandleUnauthorizedRequest</code> to see what action should occur.</p> <pre><code>protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) { // If redirect is true (default) then let base handle if (Redirect) { base.HandleUnauthorizedRequest(filterContext); return; } // else set result to empty result to prevent anything from returning from the action filterContext.Result = new EmptyResult(); } </code></pre> <h3>Usage</h3> <p>This works on any method in a <code>Controller</code>:</p> <pre><code>// Code to display an "Edit" button on our buyer's guide pages // Won't redirect user to login [AdminAuthorizeAttribute(Roles = "Editor", Redirect = false)] public PartialViewResult EditGuideButton(int id) { return PartialView(null, id); } // Code to display the actual Edit page for buyer's guides // Will redirect user to login [AdminAuthorizeAttribute(Roles = "Editor")] public ActionResult Edit(int id) { // lookup code return View(guide); } </code></pre> <p>And because of how our role rules cascade, users in the "Admin" and "Manager" roles automatically have access to these methods without having to be explicitly granted the role. </p> <h4>Dangers in Cascading</h4> <p>Cascading roles like this are not the default method, so if used be sure to make it well documented and have code to prevent users from being assigned to multiple roles which can cause some issues.</p>
Share this post:
Recent Posts By Siva Katir
Using ASP.NET Custom Authorization Attribute to Hide Page Elements
Filtering for Bots
Let Us Fill Your Excel Spreadsheet With Barcodes
Easily Generate Barcodes using Microsoft Excel for Free
Introducing POSGuys Barcode API V.0.1
Please enable JavaScript to view the comments.