Derek Stegelman

Custom Authorize Attribute in .Net MVC

River

I've recently been doing most of my coding within the .Net space. As such I've found myself trying to repeat several of the patterns available within the Django Framework. One of those common patterns is authentication/authorization and easy ways to apply these to various controllers/views.

It's sometimes necessary to restrict access to certain views based upon more than a user role. For instance, you may only want to allow the owner of an object (or and Admin) to be able to remove or modify that object within the application. In order to do so we can create a customized controller authorize attribute to complete this.

Given this object

Models.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace ECCScheduling.Models
{
    public class Observation
    {
        public Observation()
        {
            // Initial value for all observations.
            this.Cancelled = false;
        }
        public int ID { get; set; }
        public int RoomID { get; set; }
        public int ClientID { get; set; }
        public DateTime StartTime { get; set; }
        public DateTime EndTime { get; set; }
        public bool Cancelled { get; set; }

        public virtual Room Room { get; set; }
        public virtual Client Client { get; set; }

    }
}

Also given that there is a role that is assigned to your principal as "Admin".

Create the following class somewhere in your project. I like to create an Auth folder for this.

Auth/RestrictObservationToOwnerOrAdmin.cs

using System;
using System.Web;
using System.Web.Mvc;
using ECCScheduling.Models;
using ECCScheduling.DAL;

namespace ECCScheduling.Auth
{
    public class RestrictObservationToOwnerOrAdmin : AuthorizeAttribute
    {
        private ObservationRespository observationRepository = new ObservationRespository(new ECCContext());

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            string eName = httpContext.User.Identity.Name.ToString();
            // Value of the parameter being passed to the controller.
            var id = (httpContext.Request.RequestContext.RouteData.Values["id"] as string) ?? (httpContext.Request["id"] as string);

            // Observation object.
            Observation observation = this.observationRepository.GetObservationByID(Int32.Parse(id));
            bool isOwner = eName == observation.Client.eName;
            bool isAdmin = httpContext.User.IsInRole("Admin");

            if (!isOwner && !isAdmin)
            {
                return false;
            }

            return base.AuthorizeCore(httpContext);
        }
    }
}

The custom authorize attribute searches the httpContext for the ID of the item you are changing, checks it against the actual object and against the current user role before returning a result.

Then on the controller action you want to restrict:

[HttpPost]
[ValidateAntiForgeryToken]
[RestrictObservationToOwnerOrAdmin]
public ActionResult Cancel(int id)
{
    // Do stuff only that admins or owners can do...
}