开发者

How to ensure a user owns or belongs to a resource when navigating to a route (ASP.NET MVC)

I am wondering how to ensure that an employee cannot access information from another company). This is not an authentication / roles based question on applying roles or permissions to actions, but rather ensuring that the data somebody is try to access actually belongs to them.

Users belong to a department which in turn belongs to a company. In the below example the calendar belongs to a company. How do I ensure that the user can only edit calendars within their own company.

public ActionResult Edit(int? calendarId)
{
       ...
}

So when a user browses to /Calendars/55 how do I ensure that the calendar belongs to the same company as the user? So that for example, you can't tamper with the URL and put in an id for a calendar that belongs to another company.

Approach 1

public ActionResult Edit(int? calendarId)
{
    // get company that calendar belongs to
    int calCompanyId = ..
    if (CurrentUser.CompanyId != calCompanyId)
        return RedirectToAction(....); // cannot access resource

       ...
}

Whilst this would work, I'm wondering if there is a better way to handle this sort of problem. Possibly that wouldn't require putting in checks for every single action across all controllers like this.

Is there a typical pattern used to solve a problem like this where you need to check the user has access to the particular route? I would think this is a pretty typical problem for applications that have resources(companies, departments etc.) and need to ensu开发者_StackOverflow中文版re one company/user cannot access another companies data.

Edit:

As pointed out by @jfar, I could use the CompanyId but it's not available in most routes. Should I consider changing my route structure to always include this to make these checks easier? Doing this would be a fair amount of work and would probably 'uglify' the routes though.

Upon thinking about this problem further I think there may be no other choice then to put something similar to an 'IsOwner(....)' check within most actions. The reason for this is a couple fold;

  • Not all items accessible by a route have a CompanyId property on them that can be easily compared to against the user's companyId for example
  • A resource in a route might have a departmentId which is in turned owned by a company. So in this scenario I would have to implement an overloaded 'IsOwner' that knows to check a departmentId or follow the reference up to the company that owns the department

At this stage I thinking along the lines that I will have an 'IsOwner' method simlar to what @jfar posted that can check if a resource that is directly linked to a company is owned by that company, whilst providing some overloaded versions.

CompanyOwns(CurrentUser.CompanyId, model);
DepartmentOwns(CurrentUser.departmentId, model);

All my database models implement an interface that makes finding them by id easier.

public interface IAmIdentifiable<T>
{
   T Id { get; }
}

I might think about adding some more interfaces to the model to aid in the aforementioned helper process like

public interface IAmOwnedByACompany
{
   int CompanyId { get; }
}

public interface IAmOwnedByADepartment
{
   int DepartmentId { get; }
}

This will make checking objects in 'IsOwner' type methods via reflection easier. I haven't had time to completely think this through but believe @jfar was right when he said in this sort of scenario, you really do have to some form of manual checking in each method when determine whether a user should have access to a particular route (which represents a resource). By implementing some interfaces and some clever 'IsOwner' type methods, I hope to make these checks quite simple.


You could add this into your base controller. Every request, if an RouteParameter named {CompanyId} comes in, automatically check to make sure the CurrentUser.CompanyId matches it.

    protected User CurrentUser { get; set; }
    protected override void OnAuthorization(AuthorizationContext filterContext)
    {
        base.OnAuthorization(filterContext);

        if( RouteData.Values["CompanyId"] != null )
            if (CurrentUser.CompanyId != RouteData.Values["CompanyId"] )
                //Redirect to wherever

        //your not restricted from getting the companyid from the route
        //you could get the id from the logged in user, session, or any other method

    }

Update:

You could create a helper that uses reflection to check if entities have the right owner. Code written on the fly, could have an error.

    public bool IsOwner<T>(T model, int companyId)
    {
        var prop = model.GetType().GetProperty("CompanyId");

        if (prop == null)
            return false;

        var modelCompanyId = (int)prop.GetValue(model, null);

        return modelCompanyId == companyId;
    }
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜