-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Support Authorities Without Role Prefix in RoleHierarchyImpl
Builder
#15272
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
Support Authorities Without Role Prefix in RoleHierarchyImpl
Builder
#15272
Conversation
Closes spring-projectsgh-15264 Signed-off-by: Niels Basjes <[email protected]>
Hi @nielsbasjes, thanks for the report.
Would you please move this to another PR? From my perspective it isn't exactly a bug but a support that should be added to the builder. A new PR is better so we can track exactly when/what changed. |
Closes spring-projectsgh-15264 Signed-off-by: Niels Basjes <[email protected]>
ceac3fd
to
35ded39
Compare
I have removed that part from this PR. |
@marcusdacoregio is this how you want this merge request? |
Hi @nielsbasjes. I plan to review this PR before 6.4.0-M1 on July 15th. I'll leave my feedback if there is anything else to be changed. |
@marcusdacoregio How shall I submit the other change? I'm asking because it is partially overlapping code. |
RoleHierarchyImpl
Builder
core/src/main/java/org/springframework/security/access/hierarchicalroles/RoleHierarchyImpl.java
Show resolved
Hide resolved
.theseAuthorities("ROLE_A", "ROLE_B", "ROLE_C", "ROLE_D"); | ||
|
||
assertHierarchy(roleHierarchyImpl).givesToAuthorities("C") | ||
.theseAuthorities("C", "ROLE_B", "ROLE_C", "ROLE_D", "ROLE_E", "ROLE_F"); |
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.
As of now, using .authority("C").implies("E", "F", "B")
, means that C
will have ROLE_E
, ROLE_F
and ROLE_B
. What if I want to say that authority user:write
implies in user:read
, user:block
, for example?
I'm thinking that when using authority(...)
the role prefix should be ignored for the implied authorities too. It should be possible to do .authority("user:write").implies("user:read", "user:delete")
. What do you think?
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.
My mindset when writing this was that
- the authority is a "label" that is provided externally provided (via authentication).
- the role is a "label" that is internal to use to actually give access to things.
So from your example assuming the 'user:...' are externally provided authorities I see this as a possible way of doing this:
.authority("user:read") .implies("USER_READ")
.authority("user:write") .implies("USER_WRITE")
.authority("user:delete").implies("USER_DELETE")
.authority("TeamXYZMembers").implies("USER_READ", "USER_WRITE")
.role("USER_WRITE") .implies("USER_READ", "USER_DELETE")
This would make all members of "Team XYZ" have the roles USER_READ, USER_WRITE and USER_DELETE.
Primary question: Does this reasoning make sense? It is correct?
If this is correct; Perhaps it should be documented as such?
(if you want I'll put up a separate merge request with such documentation)
The way I normally structure this is that in an application I define roles being groups of users with the same set of tasks like "Administrator", "Manager", "User", etc. Those are then assigned to the user in an external IGA/IAM system (often based on department/job title/...).
Then via the authentication of the specific user; the application receives one or more of these "external roles" as the authorities via the authentication.
From there my application has something like this
.authority("User") .implies("USER_READ")
.authority("Manager") .implies("USER_WRITE")
.authority("Administrator").implies("USER_DELETE", "SHOW_DASHBOARD")
.role("USER_WRITE") .implies("USER_READ")
.role("USER_DELETE") .implies("USER_WRITE")
As said; I you want I'm willing to make a first draft on documenting this.
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.
If you want to be able to imply an authority then something like this is needed:
.authority("user:write").impliesAuthority("user:read", "user:delete")
A separate function is needed because there is no way to make any distinction between authority
or role
because they are both just a string.
Also
- I currently disagree with this approach given how I look at the
authority
(external) androle
(internal). - It is not needed to get the desired effect as shown in the example I posted yesterday.
Signed-off-by: Niels Basjes <[email protected]>
Hi @nielsbasjes. I was talking to @jzheaux about this feature and we come to a conclusion that we should refrain from adding the Our suggestion is to add a new What do you think? |
Unexpected but you're the committers here.
Where do you see "not so predictable behavior"?
From where I'm standing the confusion (which is there) stems mainly from the difference between My code and explanation (which as I said I'm willing to put into the documentation if so desired) make it clearer and reliably usable ... at least to me it does.
That would be a completely silly change because it is 100% the same as simply doing In my applications I do not want an external authority to accidentally match a role ever; I have even created this last week to make that even stronger to ensure no problems if someone creates an externally provided authority "ROLE_X":
I think it would really help if you document what you see as the distinction between |
I don't think I clearly follow the following statement:
In my opinion, this is probably mostly related to your business needs than how the framework sees it. Both authorities and roles are the same thing, sometimes it makes sense to group users into roles and sometimes you need a more granular control so you can opt-in for authorities. But that is just a matter of naming, when performing any authorization logic the framework does not differ whether it is an authority or a role.
That's the point, there is no difference between those two in general. The example that you gave earlier from my point of view should have a different arrangement. Roles are often used to group authorities and simplify access control logic: RoleHierarchyImpl.withDefaultRolePrefix()
.role("USER_WRITE") .implies("USER_READ")
.role("USER_DELETE") .implies("USER_WRITE")
.authority("User") .implies("USER_READ")
.authority("Manager") .implies("USER_WRITE")
.authority("Administrator").implies("USER_DELETE", "SHOW_DASHBOARD") should be: RoleHierarchyImpl.withNoRolePrefix()
.role("ROLE_USER") .implies("USER_READ")
.role("ROLE_MANAGER") .implies("USER_WRITE")
.role("ROLE_ADMINISTRATOR") .implies("USER_DELETE", "SHOW_DASHBOARD")
.role("USER_WRITE") .implies("USER_READ")
.role("USER_DELETE") .implies("USER_WRITE") In the example above, there is three roles and based on the role that the user has authorities are given to them. It sounds like it's the other way around than your understanding, but authorities/roles can be given to users directly, with no hard rule on whether it should be external/internal. This is also (briefly) mentioned in the documentation.
Absolutely, we can include in the reference docs some guidance on the difference between those two. I apologize for the back and forth on this, but since this is aimed for 6.4 we have a bit of time and we should take advantage of it to try to refine as much as possible. |
Yes, so that seems to be the big problem for me here; The framework has two flags that are technically identical and also do not have any logical distinction between them. That makes the current situation very confusing for me. I'm confronted with authorities which are of external origin (i.e. usergroups from the IAM/IGA solution) which I need to handle in an as clean as possible way within the application. My need is to map these external values (without any prefix; stored as authorities) into Roles in a clean way, that is what I'm trying to achieve here. Key question: What would you like to do with this? How do you see this? I'm willing to change it as long as I understand how you want it. For now: I have taken the code I put here as a starting point and created a company internal variant that meets our business needs: Mapping authorities and roles into new roles. Note: I had to copy some of the code (instead of inherit) because parts of it were |
Hi @nielsbasjes. The reference docs have a good explanation about authorities and where the With that in mind, I still think that adding that new method to the builder will cause some confusion, and therefore, I don't think we should merge it as is without more feedback from the community. For such scenarios, users can always use the constructor that accepts a role hierarchy as a I'd still love a PR to fix #15264 (comment). With that said, I'll close this as declined for now, but we can always reopen it as soon as we get more community feedback. |
@marcusdacoregio here is my take on #15264
This is 3 changes (hence 3 commits):
Are the documentation changes I created enough?