14

I am building a simple CMS in which roles are set dynamically in the admin panel. The existing way of authorizing a controller method, adding [Authorize(Roles="admin")] for example, is therefore no longer sufficient. The role-action relationship must be stored in the database, so that end users can easily give/take permissions to/from others in the admin panel. How can I implement this?

paul
  • 1,655
  • 11
  • 23
xantrus
  • 1,975
  • 1
  • 22
  • 44

3 Answers3

20

If you want to take control of the authorization process, you should subclass AuthorizeAttribute and override the AuthorizeCore method. Then simply decorate your controllers with your CmsAuthorizeAttribute instead of the default.

public class CmsAuthorizeAttribute : AuthorizeAttribute
{
    public override virtual bool AuthorizeCore(HttpContextBase httpContext)
    {
        IPrincipal user = httpContext.User;
        IIdentity identity = user.Identity;

        if (!identity.IsAuthenticated) {
            return false;
        }

        bool isAuthorized = true;
        // TODO: perform custom authorization against the CMS


        return isAuthorized;
    }
}

The downside to this is that you won't have access to ctor-injected IoC, so you'll have to request any dependencies from the container directly.

Richard Szalay
  • 83,269
  • 19
  • 178
  • 237
  • what is the downside of getting dependencies from the container directly? I have had to this for my RoleProvider implementation... – Haroon Jun 22 '11 at 09:13
  • 1
    @Haroon - The downside is one of design. It's generally considered best practice for code to remain ignorant of the IoC Container in order to reduce your dependencies on it (for example, you might want to reuse your code on WP7 where a reflection-based container is generally avoided for performance reasons). – Richard Szalay Jun 22 '11 at 13:30
  • @Haroon - Having said that, MVC 3 supports filter attribute injection via attribute-decorated properties. It's still requires knowledge of the container, but it's more easily mocked. – Richard Szalay Jun 22 '11 at 13:32
  • Thats the problem i find myself with, mvc2 feels hacky at times but i think if the job gets done using the container. I would rather not write hacky code just to make things "ideal" – Haroon Jun 22 '11 at 22:11
0

That is exactly what the ASP.NET membership / profile stuff does for you. And it works with the Authorize attribute.

If you want to roll your own you could create a custom action filter that mimics the behavior of the standard Authorize action filter does. Pseudo code below.

public MyAuthorizeAttribute : ActionFilterAttribute
{
    public string MyRole { get; set; }

    public void OnActionExecuting(ControllerContext context)
    {
        if (!(bool)Session["userIsAuthenticated"])
        {
            throw new AuthenticationException("Must log in.");
        }

        if (!Session["userRoles"].Contains(MyRole))
        {
            throw new AuthenticationException("Must have role " + MyRole);
        }
    }
}
rmac
  • 812
  • 6
  • 13
  • I'm quite new to that stuff, but I saw an example where the role was specifically assigned in the code, and I DO NOT want that. for example, some client might have a user group called "Engineer" which has specific privileges. I want him to be able to set them up from the admin panel, without touching any piece of code. Right now, I can't see how you can use the standard Authorize attribute for that – xantrus Mar 07 '10 at 21:00
  • Well then you'd have to add lookups in your database at some point, maybe match the user and controller/action names with an access rule you have in the DB. Or something like that. – rmac Mar 07 '10 at 21:07
0

The role - action relationship must be stored in the database

You will have to check your security within the controller method, unless you want to subclass AuthorizeAttribute so that it looks up the roles from the database for you.

Robert Harvey
  • 178,213
  • 47
  • 333
  • 501