1

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?

johnnyRose
  • 7,310
  • 17
  • 40
  • 61
  • 2
    so, a Blacklist instead of a Whitelist – Jonesopolis Jun 19 '15 at 16:47
  • @Jonesy - Yes, essentially. – johnnyRose Jun 19 '15 at 16:47
  • 4
    you could write your own `[BlackListAttribute]`, that accepts roles in the same fashion – Jonesopolis Jun 19 '15 at 16:49
  • agree with Jonesy you could extend a new class from AuthorizeAttribute and build your own – DarkVision Jun 19 '15 at 17:16
  • [You mean something like this?](http://stackoverflow.com/questions/1376595/asp-net-mvc-opposite-of-authorise) – Brad C Jun 19 '15 at 19:09
  • yeah exactly you need to do some logic to fetch only blacklist people you don't want to access that controller or specific function – DarkVision Jun 19 '15 at 19:19
  • Check out this post: http://stackoverflow.com/questions/1376595/asp-net-mvc-opposite-of-authorise – uhum Jun 19 '15 at 21:00
  • 1
    We consider what .Net calls a "role" a permission, we grant permission to Users, so rather than saying [Authorize(Roles = "Admin")] we use [Authorize(Roles = "Home.About")]. Now what we call "Roles" is a set of permissions. User's are put in our "roles". – PilotBob Jun 19 '15 at 21:13

2 Answers2

2

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();
}
uhum
  • 177
  • 2
  • 8
  • 2
    yeah, maybe you could create your own answer and post it instead of commenting something that is not constructive at all. – uhum Feb 17 '18 at 18:46
1

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();
    }
}
johnnyRose
  • 7,310
  • 17
  • 40
  • 61