Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is that RESTful to limit resource's field visibility per authenticated user Role?

I'm building quite complex REST API. The whole API is protected with authentication. Some of the resources (like, let's say, Person) should be accessible for anyone in the system, however I'd like to "hide" some fields for specific user's roles.

Let's say the Person resource has following fields:

FirstName
LastName
BirthDate
Address

I'd like them all to be visible for users with HRManager role, hide Address for JuniorHRManager and leave FirstName + LastName for everyone else.

Is that RESTful way to just remove fields from the response based on rules applied for the role which logged in user has? This would be most simple to implement I guess (since I'm using an excellent ServiceStack which has global response filters), yet I'm not sure if that doesn't break the REST rules?

The only other way I've so far thought of is creating role-specific Resources (like PersonForHRManager etc.) however this would be ridiculous as the system is supposed to have variety of combinations of visible & hidden fields for roles.

like image 936
migajek Avatar asked Dec 26 '13 09:12

migajek


People also ask

What should rest API return?

The API should always return sensible HTTP status codes. API errors typically break down into 2 types: 400 series status codes for client issues & 500 series status codes for server issues. At a minimum, the API should standardize that all 400 series errors come with consumable JSON error representation.

What is the recommended method and URL pattern for retrieving a specific user?

The GET /api/v2/users/{id} endpoint allows you to retrieve a specific user using their Auth0 user ID. This endpoint is immediately consistent, and as such, we recommend that you use this endpoint for: User searches run during the authentication process.

What is REST URL?

Although URLs containing parameters within the query string do themselves conform to REST constraints, the term “REST-style URL” is often used to signify a URL that contains its parameters within the URL file path, rather than the query string.


2 Answers

I agree with your approach, the response filter would probably be the best solution to do this; and simply mark up the response DTO with an attribute describing the required roles. I haven't seen a better way to do property level permissions.

It is perfectly acceptable to remove properties from response based on roles, if it is a public API just make sure to document the properties people can expect back in each role.

Response Filter, in your AppHost:

this.ResponseFilters.Add((req, res, dto) => {

    // Get the roles you are permitted to access. You will need to store these in the request Items collection
    var roles = (from r in req.Items where r.Key == "Roles" select r.Value).FirstOrDefault() as string[];

    // Get the type of the response dto
    var dtoType = dto.GetType();

    // Loop through the properties
    foreach(var property in dtoType.GetPublicProperties()){

        // Ignore properties that are read-only
        if(!property.CanWrite)
            continue;

        // Get all the role attributes on the property
        var attributes = property.GetCustomAttributes(typeof(RequireRoleAttribute), false) as RequireRoleAttribute[];

        // Get all the permitted roles
        var permittedRoles = new List<string>();
        foreach(var attribute in attributes)
            permittedRoles.AddRange(attribute.Roles);

        // Check if there are specific permitted roles assigned to this attribute
        if(permittedRoles.Count != 0)
        {
            bool permitted = false;

            // Check if check require role against roles we may have.
            foreach(var role in permittedRoles){
                if(roles.Contains(role))
                {
                    // We have a matching role
                    permitted = true;
                    break;
                }
            }

            // No permission to the property
            if(!permitted) {
                var type = property.GetType();

                // Set the field to a default value.
                property.SetValue(dto, null);
            }
        }
    }
});

The attribute:

public class RequireRoleAttribute : Attribute
{
    public string[] Roles { get; set; }
    public RequireRoleAttribute(params string[] roles) { Roles = roles; }
}

On the DTO:

[RequireRole("Spiderman","Superman","Batman")]
public string Address { get; set; }

Notes:

  • You will need to save you permitted roles into the request.Items.Add("Roles", string[])
  • I haven't tested the above code, so it may not be perfect, but it should be pretty close.

I hope this helps.

like image 108
Scott Avatar answered Nov 15 '22 23:11

Scott


I think you got it right. RESTful services is nothing more than simple HTTP requests. Therefore, you have to protect http resources as a whole. I see two possible strategies:

  1. Implement different RESTful service for each role, which is the strategy you suggest. This is not very easy to maintain, as you probably understand.

  2. Implement one RESTful service which returns for all roles but you would have to check for authorization while forming each property value. For example, when you shape the value to be returned for the property Address on the server side, you will have to check the user's role. If the user is an HRManager you would return the right value. If the user is a JuniorHRManager you would have to return the specific property empty or with a respective message indicating that the user is not allowed to access the specific property. This strategy could be easier to maintain, depending on the server side technology used to implement the web service (I don't know which you are using). In .NET for example you could use attributes for each property indicating which authorization roles have access to a specific property. If you use .NET, this and this tutorials could provide some extra guidance.

Hope I helped!

like image 21
Pantelis Natsiavas Avatar answered Nov 15 '22 23:11

Pantelis Natsiavas