ASP.NET MVC Blacklist for Roles/Users

2k views Asked by At

Question Summary: In ASP.NET MVC, is there a clean way to prevent a specific user or role from accessing an action?

Obviously, the following would allow roles Admin and Editor to access the entire controller.

[Authorize(Roles = "Admin, Editor")]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    public ActionResult About()
    {
        return View();
    }
}

If I only wanted the Admin role to have access to the About action, I could do the following:

[Authorize(Roles = "Admin, Editor")]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }

    [Authorize(Roles = "Admin")] // this will take precedence over the controller's authorization
    public ActionResult About()
    {
        return View();
    }
}

Is there a way to accomplish this without listing every single role that needs access, and only specifying the roles that should be prevented from having access?

2

There are 2 answers

0
johnnyRose On BEST ANSWER

Here is the code for the class I used to solve this problem. It derives heavily from AuthorizeAttribute, and will allow any authenticated user through who does not match the specifications set by the parameters.

(Note that the important method is AuthorizeCore - everything else is essentially copied or inherited from AuthorizeAttribute)

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]
public class BlackListAttribute : AuthorizeAttribute
{
    private static readonly string[] _emptyArray = new string[0];

    private string _roles;
    private string _users;

    private string[] _rolesSplit = _emptyArray;
    private string[] _usersSplit = _emptyArray;

    public new string Roles
    {
        get { return _roles ?? String.Empty; }
        set
        {
            _roles = value;
            _rolesSplit = SplitString(value);
        }
    }

    public new string Users
    {
        get { return _users ?? String.Empty; }
        set
        {
            _users = value;
            _usersSplit = SplitString(value);
        }
    }
    // This is the important part. Everything else is either inherited from AuthorizeAttribute or, in the case of private or internal members, copied from AuthorizeAttribute.
    protected override bool AuthorizeCore(System.Web.HttpContextBase httpContext)
    {
        if (httpContext == null)
        {
            throw new ArgumentNullException("httpContext");
        }

        IPrincipal user = httpContext.User;

        if (user == null || user.Identity == null || !user.Identity.IsAuthenticated)
        {
            return false;
        }

        if (_usersSplit.Length > 0 && _usersSplit.Contains(user.Identity.Name, StringComparer.OrdinalIgnoreCase))
        {
            return false;
        }

        if (_rolesSplit.Length > 0 && _rolesSplit.Any(user.IsInRole))
        {
            return false;
        }

        return true;
    }

    internal static string[] SplitString(string original)
    {
        if (String.IsNullOrEmpty(original))
        {
            return _emptyArray;
        }

        var split = from piece in original.Split(',')
                    let trimmed = piece.Trim()
                    where !String.IsNullOrEmpty(trimmed)
                    select trimmed;
        return split.ToArray();
    }
}

You can use it on controllers or actions like any other AuthorizeAttribute:

[Authorize(Roles = "Admin, Editor")]
public class HomeController : Controller
{
    public ActionResult Index()
    {
        return View();
    }
    [BlackList(Roles = "Editor")]
    public ActionResult About()
    {
        return View();
    }
}
1
uhum On

Create your own blacklist class just like this one:

public class Blacklist : AuthorizeAttribute {
    private List<string> RolesList;
    public string Roles {
        get {
            string roles = "";
            if (RolesList!= null && RolesList.Count > 0) {
                int counter = 0;
                foreach (string role in RolesList) {
                    counter++;
                    if (counter == RolesList.Count)
                        roles = role;
                    else 
                        roles += role + ",";
                }
            }
            return roles;
        }
        set {
            RolesList = new List<string>();
            string[] roles = value.Split(',');
            foreach (string role in roles) {
                RolesList.Add(role);
            }
        }
    }
//constructor 
    public Blacklist () {
        RolesList = new List<string>();
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext) {
        bool result = true;
        if (httpContext == null) {
            throw new ArgumentNullException("httpContext");
        }
        foreach (string role in RolesList) {
            if (httpContext.User.IsInRole(role)) {
                result = false;
                break;
            }
        }
        return result;
    }
}

Now you are going to block the roles you want:

[Authorize]
[Blacklist (Roles = "Admin", "Editor")]
    public ActionResult Index() {
    return View();
}