0

Is the following API of a claims authorization service ok, from the point of view of usability?

/* before UPDATE it was like this:  
var canEdit = Authz.ForNewRequest()
    .WithActionName("edit")
    .WithResourceName("project")
    .WithResourceClaim("DepartmentId","21")
    .CheckAccess(); */

//UPDATE:
var deptId = SomehowGetDepartmentIdAtRuntime(targetProject);
var canEdit = Authz.ForNewRequest()
    .WithActionName("edit")
    .WithResourceName("project")
    .WithResourceClaim("DepartmentId",deptId)
    .CheckAccess();

 if (canEdit) 
 {
     //edit project
 }

And configuration like this:

 var authorizationModel = Authorization.ConfigNamespace.AuthzConfig
     .LoadAuthorizationModelFromXml("authz.xml");

 Authorization.ConfigNamespace.AuthzConfig
     .SetApplicationAuthorization(authorizationModel);

Or custom configuration like this:

 var authzCustomConfig = Authorization.ConfigNamespace.AuthzConfig
     .NewCustomConfiguration()
     .WithCustomClaimBasedFactFunctions(claimBasedFunctions)
     .WithCustomClaimProviders(claimProviders)
     .WithCustomCompositeFactFunctions(compositeFactFunctions)
     .WithCustomObligations(obligations);

 var authorizationModel = Authorization.ConfigNamespace.AuthzConfig
     .LoadAuthorizationModelFromXml("authz.xml", authzCustomConfig);

 Authorization.ConfigNamespace.AuthzConfig
     .SetApplicationAuthorization(authorizationModel);

Basically, the question is about the top part of the iceberg, i.e. how to use the service, but not how to implement or design the inner part. But just in case, here are a couple of general words about this service:

This service gives an answer of true/false for the given authorization request.

Authorization request has information about:

  • Action (action name, for example)
  • Resource (resource name, resource properties)
  • Subject (user name, user id, user roles, user properties)

Due to the fact that i use Microsoft.IdentityModel:

  • The properties of an action, resource, or a subject are presented as Claims. Approximately, a claim is an object, which has a value, value name, and value type. For example, a claim for a user could have the following info: ("UserName", "Andrey", "string").
  • The authorization request is the AuthorizationContext class from the Microsoft.IdentityModel.Claims namespace.

Two more things to consider:

  • I'd like this service to be a general solution, which could fit not only to one certain project.
  • Logic could be involved in order to understand if a condition for the permission decision is met or not.

That's why custom logic might be injected. Those claimBasedFunctions, claimProviders, compositeFactFunctions, and obligations are the custom logic. But they don't really matter for the question, just some custom confuguration elements, implementations of the interfaces, which are defined in the authorization assembly. The question is not about what they should be, or how they should work. You can think of them as of any interface implementatons that have to be injected.


Thanks!

P.S. this question is off-topic for the Code Review site.

Andrey K.
  • 665
  • 8
  • 29

1 Answers1

1

If I interpret your description correctly, the following code means: "If you want to edit a project, you need a claim with name DepartmentId that has a value of 21.

 var canEdit = Authz.ForNewRequest()
     .WithActionName("edit")
     .WithResourceName("project")
     .WithResourceClaim("DepartmentId","21")
     .CheckAccess();

 if (canEdit) 
 {
     //edit project
 }

This statement would be at the start of your Edit action in your Project controller in case you were building an MVC application.

If my interpretation is correct, I would advise you to remove the WithActionName and WithResourceName methods. Those things can be retrieved from the context in which this code is executing. Your fluent API is too easy to copy from one method to another and forget to update those strings. I would look at a custom authorize attribute that you attach to an action in which you checks the claims.

UPDATE: I was thinking something like this:

public class ProjectController : ApiController
{
    [HttpPost]
    [MyAuthorize("DepartmentId","21")
    public void Edit(string applicationName)
    {
        // business logic
    }
}

Inside the MyAuthorize attribute implementation you can retrieve the controller- and action names. If the developer using the attribute doesn't have to specify this, he/she can't get it wrong.

Community
  • 1
  • 1
MvdD
  • 22,082
  • 8
  • 65
  • 93
  • Thanks! I'd like to make it more clear for me, sorry. 21 is the _Project's department id_. Check access for _editing_ a _project_ may say _NO_. Check access for _resource_ with _departament id_ = 21 may also say _NO_. But alltogether may result in a _YES_. And _project_'s _department id_ is known at **runtime**. So do you mean to have **both**: 1) an _attribute_, declaring the _action_ and _resourse_ above a method; and 2) the `CheckAccess() //knowing dept id` call from within this method, implying that `CheckAccess()` extracts some metadata from the method's attribute above? – Andrey K. Mar 11 '16 at 10:12
  • I've updated the answer to make it more clear (hopefully). – MvdD Mar 12 '16 at 00:44
  • Thanks! It's probably a good solution, but sorry, i don't fully understand how to use it in my case. I made an update in my question in order to say: 21 is not known at compile time. It should be something like `[MyAuthorize("DepartmentId",x)]`, but i'm not sure whether this is possible with c#. My authorization is probably a bit harder one because logic is involved. For example, the project's department must be subordinate to the authenticated user's one in order to be edited. – Andrey K. Mar 12 '16 at 19:29
  • Oh, forgot to say that there could be possibly be hundreds of different departments in a general case. – Andrey K. Mar 12 '16 at 19:34
  • The logic in your attribute implementation can be as complex as needed. It could query some service for the needed info if necessary. My main advice was to make sure the developer has little possibility to make mistakes when doing authorization. Especially when developers do not completely understand what your canEdit statement means, they are inclined to just copy/paste it into the next method. – MvdD Mar 14 '16 at 05:02