Is it possible to add roles to commands and queries?

Greetings,

I heard there was a way to add roles to commands and queries, but I can’t find any documentation on this. Does anyone know how to do it?

I wish to use these roles to restrict access to certain commands and queries, for example by having an admin role.

3 Likes

Thanks for your question, @AirmanBugs!

To restrict the right to issue commands or queries to certain users (e.g. to users with the admin-role only) you write a SecurityDescriptor for that Securable.

This is part of the Dolittle.Fundamentals -package and will be run on every request to check the access to the Command (or query).

There is a description of it here: https://dolittle.io/getting-started/guides/security/

If you have any issues using it, don’t hesitate to contact me!

4 Likes

Just what the doctor ordered! I’ll check a look and get back to you if I run into any issues. Thanks, @tomas!

4 Likes

@tomas I have successfully locked myself out! This is great, but now I would love to assign myself the role of admin to issue the command. I am new to claims and I’m not sure how role claims are made. This might not be directly Dolittle related, but do you maybe have any pointers? Specifically, how does Dolittle resolve what roles the current user has and how can I claim that a user has a certain role?

Additional info:
We are using Auth0 to make claims. Auth0 allows us to set a role, but I don’t know the implication of these roles. I am able to put user metadata into the claim and inspect it in the backend, but I suspect this is not the best approach to handle roles.
We have the basic implementation of PrincipalResolver as described in the article you linked.

1 Like

Using Auth0 means you’re using OpenIDConnect (OIDC), so the roles should be transferred to you as claims in the identity token.

I have not used Auth0 myself, but when I’ve used OIDC with roles the token will include role claims. I suggest you assign yourself the “admin” role in Auth0 (however you do that), or assign yourself another, new role and check for that in your SecurityDescriptor.

A very nice tool to see what Auth0 is actually sending to your application is https://jwt.io - here you can paste the JSON web token in and it will decipher it for you and show you what is actually reaching your application. You will find a set of claims in there that should correspond to the data Auth0 transfers. It is probably a set of claims of the type “role”.

Here’s a nice article about claims in the jwt and here’s the spec for the “standard” claims.

2 Likes

I’m still struggling, but it looks like I’m getting closer.
I still haven’t figured out how to set a role claim (might be a restriction of the free Auth0 version), but I have been able to add new custom claims. So therefore I started using HasClaimTypeWithValue(). The result is that the SecurityDescriptor is no longer rejecting any commands, for some reason.

I have verified that I have a claim with the right type and value through the debugger in Rider. I have also verified that HasClaimTypeWithValue() returns what I expect it to. But regardless of what I put into its arguments in the code below, my commands pass security.
I am getting similar results with HasClaimType().

public class RestrictAccessControlCommandsToAdminRole : SecurityDescriptor
    {
        public RestrictAccessControlCommandsToAdminRole(ICanResolvePrincipal principalResolver)
        {
            When
                .Handling()
                .Commands()
                .InNamespace(
                    typeof(RestrictAccessControlCommandsToAdminRole).Namespace,
                    restriction =>
                        restriction
                            .UserFrom(principalResolver)
                            .HasClaimTypeWithValue("http://test", "foo")
                );
        }
    }

Any ideas?

Try this instead:

When
    .Handling()
    .Commands()
    .InNamespace(
       typeof(RestrictAccessControlCommandsToAdminRole).Namespace,
       securable => 
           securable
               .UserFrom(_principal_resolver) // add claims below like this
               .MustHaveClaimTypeWithValue("name", "Tomas Ekeli")
               .MustHaveClaimTypeWithValue("preferred_username", "tomas@dolittle.com")
               .MustHaveClaimTypeWithValue("http://test", "foo") // etc etc
            );
1 Like

Praise be, it works! Thanks, Tomas.

I can see that this is a little confusing, especially when using intellisense for discoverability.

The “HasClaimTypeWithValue” is a convenience method to check the claims of the Principal (since the Principal is wrapped into the UserSecurityActor). It simply returns a boolean flag.

The “MustHaveClaimTypeWithValue” is what creates the rule that can be broken. Internally it calls “HasClaimTypeWithValue” and creates a BrokenRule if the result is false.

Think of it as unit testing and asserts. The first method is simply the answer, true or false. The second is the Assert that makes the test fail.

@tomas We should look at this interface and see if there is something we can do to make it more intuitive.

2 Likes

I actually discovered it through the documentation (the article Tomas provided), not through IntelliSense. So this means the example code in the docs should also use MustHaveClaimWithType(), right?
image

If I had been aware of both methods, I believe I would’ve found it intuitive to use the one with Must. The Has looks like it would work and it compiles, so it was hard to figure out what I had done wrong.

1 Like

Oops, we will fix that!

For future reference, I just figured out how to make a Role claim that works with MustBeInRole. I had to make the type of the claim (Claim.Type in C#) the following URI: ‘http://schemas.microsoft.com/ws/2008/06/identity/claims/role

1 Like