How to use custom Authorize attribute for roles as well as a specific user?

56,281

Solution 1

You could write a custom authorize attribute:

public class AuthorizeAdminOrOwnerOfPostAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var authorized = base.AuthorizeCore(httpContext);
        if (!authorized)
        {
            // The user is not authenticated
            return false;
        }

        var user = httpContext.User;
        if (user.IsInRole("Admin"))
        {
            // Administrator => let him in
            return true;
        }

        var rd = httpContext.Request.RequestContext.RouteData;
        var id = rd.Values["id"] as string;
        if (string.IsNullOrEmpty(id))
        {
            // No id was specified => we do not allow access
            return false;
        }

        return IsOwnerOfPost(user.Identity.Name, id);
    }

    private bool IsOwnerOfPost(string username, string postId)
    {
        // TODO: you know what to do here
        throw new NotImplementedException();
    }
}

and then decorate your controller action with it:

[AuthorizeAdminOrOwnerOfPost]
public ActionResult EditPosts(int id)
{
    return View();
}

Solution 2

I understand that you have already accepted an answer, and this was posted a while back.. (btw:excellent answer for adding custom attributes), However I would point out the following:

If you are using this attribute once. On a Single method. This isn't a good implementation. Instead you should have:

[Authorize]   // Just make sure they are auth'ed at all.
public ActionResult EditPosts(int id)
{
    Post SomePost = findPostByID (id);   // However you do it - single lookup of post

    if (!user.IsInRole("Admin") &&  !{IsOwnerOfPost(post)} )  Return Not Authorized

  ... Edit post code here
}

This has the advantages of:

  1. No additional class that someone will later wonder where it is used.
  2. No class that isn't usable anywhere else (you don't gain reuse with a custom attribute)
  3. Performance is better: Single fetch of the Post
  4. Way easier for someone to read/figure out how it works. No magic code to track down.
  5. And Years later, when HttpContextBase class doesn't exist, or other parts of the tricks used to fetch the Id parameter are gone, the code still works...
Share:
56,281
Freeman
Author by

Freeman

C# \ .NET Developer, Silverlight, WPF, Windows Phone, ASP.NET MVC, SQL Server, Windows Server, and other .NET stuff while trying to keep in mind clean and optimized code. Also a bit of a C++ amateur as well.

Updated on July 31, 2020

Comments

  • Freeman
    Freeman almost 4 years

    I have my Action Method

    [Authorize(Roles="Admin")]
    public ActionResult EditPosts(int id)
    {
        return View();
    }
    

    In my case I need to authorize administrators so they can edit posts but (here comes the cool part), I also need to allow the creator of the post to be able to edit the post which is a normal user. So how can I filter out the user that created the post as well as the admins but leave the others unauthorized? I am receiving the PostEntry id as a route parameter but that's after the attribute and also attributes only accept constant parameters, looks like something very difficult, your answers are highly appreciated, Cheers!

  • Royi Namir
    Royi Namir over 9 years
    Why // Now id was specified => we do not allow access ?
  • Royi Namir
    Royi Namir over 9 years
    Now I see you need to change : // Now id was specified to NO id was specified....(it seems that if there is ID we dont allow access)
  • cyclical
    cyclical about 9 years
    Thank you -- cuts through the flak that is out there and gets right to the point!
  • Sorangwala Abbasali
    Sorangwala Abbasali over 7 years
    This post is very useful as it helped me to make possible to allow all the roles in the mvc application except the "user" role for some controller.