-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[AC-1387] Resource-based authentication for Groups #2845
Conversation
Thank you for doing this! Permissions are something I've wanted to clean up for a while considering how difficult they are to work with. I'm in favor of your idea behind having resources and operations for permission checks. We have a problem with intermingling user role code and custom permissions that lead to missed edge cases and bugs. Do you have an idea yet of all the mappings between actions and current custom permissions? I would take that to design for feedback before starting, considering how many cases you'll have to account for. There are also areas we don't have permissions for yet, such as billing, that are mainly role based (which you could still create an auth handler for, just checking roles behind the scenes). Another interesting point to consider is that there are multiple relationships to edit on a domain object such as a Collection. You can edit the Users, you can edit the associated Groups, or you can edit the domain object itself. I don't know if we want to break it down into this level of granularity, but without it there are questions we have to answer for the clients. For instance, if you can "Manage Groups" but not "Manage Collections", can you change the associated Collections on a Group? Does "Manage Groups" implicitly give you the "View All Collections" permission? Today it does. I don't really have any opinions about the use of the built in authorization service, but seems to be a good idea to me! Also, you can check out Danielle's RBAC work for the Admin Portal here if you're interested. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I totally love this. It puts all this business logic into Requirements and their specified handlers so it makes it really easy to make them into policies that we can apply with an [Authorize]
filter if it's simple enough. You specific ones have to do with more advanced scenarios though so policies are complex enough.
I really like where this is headed.
src/Core/OrganizationFeatures/AuthorizationHandlers/OrganizationAuthHandler.cs
Outdated
Show resolved
Hide resolved
src/Core/OrganizationFeatures/AuthorizationHandlers/GroupAuthHandler.cs
Outdated
Show resolved
Hide resolved
src/Core/OrganizationFeatures/AuthorizationHandlers/GroupAuthHandler.cs
Outdated
Show resolved
Hide resolved
src/Core/OrganizationFeatures/AuthorizationHandlers/GroupOperations.cs
Outdated
Show resolved
Hide resolved
src/Core/OrganizationFeatures/AuthorizationHandlers/GroupOperations.cs
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work! Let my 2 cents as well, but I think this is a clear step up.
src/Core/OrganizationFeatures/AuthorizationHandlers/GroupAuthHandler.cs
Outdated
Show resolved
Hide resolved
src/Core/OrganizationFeatures/AuthorizationHandlers/GroupAuthHandler.cs
Outdated
Show resolved
Hide resolved
src/Core/OrganizationFeatures/AuthorizationHandlers/OrganizationOperations.cs
Outdated
Show resolved
Hide resolved
To answer your second question first:
I think there are 2 questions in here:
As above - I'd just maintain current behaviour. Where there are role based checks only, I'd come up with sensible operation names for them (e.g. view subscription, edit subscription, edit payment method, view billing history) and then move the role based check into the auth handler. It's not too different to what I've done here - "ManageGroups" is very broad, I've broken it up into granular operations and then moved that check to the handler. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great! My .Net is a bit rusty, but we did role-based authentication at a previous company and there are a decent amount of similarities here. I only have one question on a potential improvement!
src/Core/OrganizationFeatures/AuthorizationHandlers/GroupAuthorizationHandler.cs
Outdated
Show resolved
Hide resolved
This reverts commit 1ce0c89.
- Add repository method to get GroupUser - Add AuthorizationHandler for GroupUser - Move group-organization-organizationUser validation into controllers
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So much string
Guid
replacement and I love it.
src/Core/OrganizationFeatures/AuthorizationHandlers/CombinedAuthorizationHandler.cs
Show resolved
Hide resolved
src/Core/OrganizationFeatures/AuthorizationHandlers/CombinedAuthorizationHandler.cs
Outdated
Show resolved
Hide resolved
src/Core/OrganizationFeatures/AuthorizationHandlers/GroupOperations.cs
Outdated
Show resolved
Hide resolved
Thanks for the review @justindbaur, all your feedback should be addressed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, very nice work!
await _bitAuthorizationService.AuthorizeOrThrowAsync(User, group, new[] | ||
{ | ||
GroupUserOperations.Create, | ||
GroupUserOperations.Delete | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mismatch between resource and operation type
var success = org.Type == OrganizationUserType.Owner || | ||
org.Type == OrganizationUserType.Admin || | ||
org.Type == OrganizationUserType.Manager || | ||
org.Permissions.ManageGroups || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Permissions
only exists when org.Type == OrganizationUserType.Custom
otherwise its null and will throw an exception here. (I just ran into this myself in the collection access handler)
Probably easiest to just group all of the org.Permissions.*
checks and &&
them with a null check when there are this many permissions to check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I instantiated a default Permissions
object on CurrentContentOrganization
so it'll never be null :) no null checks required!
New Issues
Fixed Issues
|
We implemented this pattern for collections as part of Flexible Collections. We have a spike to review that as a team before rolling the pattern out to other resources. In the meantime, this PR served its purpose as a proof of concept and will be closed. |
Type of change
Objective
This is a proposal for us to adopt Resource-based authentication to manage our permission checks on the server.
Outline
The basic outline of this is:
Controllers can then call
_authorizationService.AuthorizeAsync(user, resource, operation)
and get back a success or failure result.Implementation notes
I've implemented this on the Groups controller as a proof of concept.
A few further notes on choices I made here (opinionated):
authorizationService
.CurrentContext
. In particular, I think the getters onCurrentContext
can obscure what permissions are really being granted to whom. In this model,CurrentContext
is mostly used to construct objects from claims and make them available for auth handlers.NotFoundException
(unless there's a reason we use this?)Benefits
I think this is an improvement because:
currentContext
getters.CurrentContext
exposes a mixture of permissions (ManageGroups
) and roles (OrganizationAdmin
). This is difficult to keep track of and change. Authorization handlers provide a central location to manage permissions for each resource.CurrentContext
has too many duties and has too much indirection in it (getters are several layers deep). Auth handlers can be tightly scoped to their resource and particular operations (not just "manage"), making them easy to understand and less risky to changeDrawbacks
Code changes
CurrentContext.ManageGroups
logic.ResourceAuthorizationFailedException
CurrentContext
so we can access organizations constructed from claimsBefore you submit
dotnet format --verify-no-changes
) (required)