20

I have a controller method called Edit in which the user can edit data they had created like so ...

public ActionResult Edit(int id)
{
    Submission submission = unit.SubmissionRepository.GetByID(id);
    User user = unit.UserRepository.GetByUsername(User.Identity.Name);

    //Make sure the submission belongs to the user
    if (submission.UserID != user.UserID)
    {
        throw new SecurityException("Unauthorized access!");
    }

    //Carry out method
}

This method works fine however it is a little messy to put in every controller Edit method. Each table always has a UserID so I was wondering if there was an easier way to automate this via an [Authorize] Attribute or some other mechanism to make the code cleaner.

4 Answers 4

32

Yes, you could achieve that through a custom Authorize attribute:

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var authorized = base.AuthorizeCore(httpContext);
        if (!authorized)
        {
            return false;
        }

        var rd = httpContext.Request.RequestContext.RouteData;

        var id = rd.Values["id"];
        var userName = httpContext.User.Identity.Name;

        Submission submission = unit.SubmissionRepository.GetByID(id);
        User user = unit.UserRepository.GetByUsername(userName);

        return submission.UserID == user.UserID;
    }
}

and then:

[MyAuthorize]
public ActionResult Edit(int id)
{
    // Carry out method
}

and let's suppose that you need to feed this submission instance that we fetched into the custom attribute as action parameter to avoid hitting the database once again you could do the following:

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var authorized = base.AuthorizeCore(httpContext);
        if (!authorized)
        {
            return false;
        }

        var rd = httpContext.Request.RequestContext.RouteData;

        var id = rd.Values["id"];
        var userName = httpContext.User.Identity.Name;

        Submission submission = unit.SubmissionRepository.GetByID(id);
        User user = unit.UserRepository.GetByUsername(userName);

        rd.Values["model"] = submission;

        return submission.UserID == user.UserID;
    }
}

and then:

[MyAuthorize]
public ActionResult Edit(Submission model)
{
    // Carry out method
}
Sign up to request clarification or add additional context in comments.

5 Comments

Thank you! I am going to make this more generic. Like have submission implement an interface called IUserOwnable which has a UserID. And then pass in the repository into the Attribute where it fetches an IUserOwnable and compared that UserID of the user to the UserID of the IUserOwnable
Sure, this could be made as generic as you like to suit your specific requirements.
Nice approach. I was hopping that by now it will have a built in attribute for that
How would you cache this so that it doesn't need to check the db every time there is an authorizer attribute for this particular submission_id and user_id? @DarinDimitrov
@DarinDimitrov what is the best way to do this .net core, in a web API with JWT Token based authentication?
2

I would suggest you pull the logic out of the action/controller and build a domain class to handle that logic.

Action methods should really only deal with getting data from and sending data to the view. You could create something generic enough to handle your needs but will also follow the single responsibility principal.

public class AuthorizedToEdit 
{
     protected override bool AuthorizeCore(string user, int itemId)
     {
         var userName = httpContext.User.Identity.Name;

         var authUsers = SubmissionRepository.GetAuthoriedUsers(itemId);

         return authUsers.Contains(user);
     }
}

This would also allow you to have the flexibility later on to allow something like admin users

Comments

0
@if (Request.IsAuthenticated && User.IsInRole("Student"))
    {
    @Html.ActionLink("Edit", "Edit", new { id = item.StdID })
    }

in my case, the loggedIn user is a student. so i say if the login request is authenticated, and if his role is student, then let the link for edit be accessible to him.

this below allows you to let the ordinary user OR the Admin perform edit also.

@if(Request.IsAuthenticated && User.IsInRole("Student") || 
User.IsInRole("Administrator"))
{
 @Html.ActionLink("Edit", "Edit", new { id = item.StdID })
}

Comments

-1

I recommend reading up on the AuthorizeAttribute (see here). Also, have you seen this post? It goes over how to override the authentication attribute innards and how to use IPrincipal and IIdentity.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.