-
Notifications
You must be signed in to change notification settings - Fork 25k
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
Handle has privileges API response for LimitedRole
#37831
Handle has privileges API response for LimitedRole
#37831
Conversation
With introduction of `LimitedRole` for API keys, the effective permissions are constrained to the permissions defined by the limit by role. This impacts the way we provide response for Has Privileges API. This change introduces few helper methods on `Role` object which can be used to get resources privileges (index / application). The methods are overridden in `LimitedRole` to take an intersection of the privileges and return the result. The `LimitedRole` changes also impact Get privileges API but we would not be handling that case. The API is primarily aimed at internal(Kibana) usage, the API is not supposed to be used for authorization decisions and has been documented as such.
Pinging @elastic/es-security |
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 left a bunch of mostly style comments. Overall I think this is the right approach.
/** | ||
* A generic structure to encapsulate resources to {@link ResourcePrivileges}. | ||
*/ | ||
public final class ResourcesPrivileges { |
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 know we do it in a few other places, but I find this name of ResourcePrivileges
and ResourcesPrivileges
to be confusing and easy to mistake. It took me a several reads of the new Javadoc that refers to both in classes in the same sentence (example).
When the plural is at the end, it's not so bad, but here we need to pluralise in the middle, and I (personally) think that's too hard to notice. I'd rather we rename this class, even if it's just ResourcePrivilegesMap
or something.
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.
Yes, after going through your comment I did feel taxing to understand, so thank you for pushing the naming. I have addressed this.
* @param storedPrivileges stored {@link ApplicationPrivilegeDescriptor} for the application | ||
* @return an instance of {@link ResourcesPrivileges} | ||
*/ | ||
public ResourcesPrivileges getResourcePrivileges(final String applicationName, List<String> forResources, |
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 think calling this a get
method underplays what it does, and assumes that caller knows what "ResourcePrivileges" is supposed to represent.
checkResourcePrivileges
or checkResourceAccess
feel better to me.
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.
+1, changed the method to reflect what it does. Thank you.
* @param forResources list of application resources | ||
* @param forPrivilegeNames list of application privileges | ||
* @param storedPrivileges stored {@link ApplicationPrivilegeDescriptor} for the application | ||
* @return an instance of {@link ResourcesPrivileges} |
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.
(Begin rant)
I don't feel as though these @param
annotations really add much. They're more or less repeating what is obvious from the type and name.
The most obvious example:
@param applicationName application name
but even
@param storedPrivileges stored {@link ApplicationPrivilegeDescriptor} for the application
We know it's stored because the parameter is named stored...
and we know they're ApplicationPrivilegeDescriptor
object from the parameter type. The javadoc should be adding additional details that aren't implied by the name/type.
I think it's good to document some of these, particularly storedPrivileges
because it isn't obvious (on the face of it) why a caller needs to pass in a bunch of privilege objects to this method, but the doc above doesn't explain that.
(End rant: It's not specifically this PR, it comes up in other places, I just happen to have some spare time today so I'm taking the opportunity to express myself)
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.
Sorry for this, I think I was not good with the documentation here. I have updated the documentation with more information to express the intent of the parameters and explain how it is being used. Thank you.
+ applicationName; | ||
assert checkPrivilege.name().equals(nameSet) : "Privilege " + checkPrivilege + " should have name " + nameSet; | ||
|
||
ResourcePrivileges.Builder builder = ResourcePrivileges.builder().setResource(checkResource); |
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.
Suggestion: I think this would read better (and be a safer API) if the resource name was a pararmeter to the builder
method rather than a set
call.
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.
Yes, I did not realize it while writing the code but your suggestion is spot on. Thank you.
builder.addPrivilege(checkPrivilegeName, Boolean.FALSE); | ||
allowAll = false; | ||
} | ||
result.compute(checkResource, (k, v) -> (v == null) ? builder.build() : builder.addPrivileges(v.getPrivileges()).build()); |
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 find this a little weird.
I guess it's probably come about because this was refactored from existing code, and the changed, but it seems a bit strange to keep putting stuff in and out of the map like this when we have a Builder class that can have stuff added to it.
This method causes a lot of object churn that isn't really necessary.
Did you consider having the map values be Builder
object instead, and then just do a final .build()
pass at the end of the method?
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 have addressed this with Builder
object as suggested here and other places, Thank you.
privileges.put(privilege, Boolean.TRUE); | ||
} else { | ||
privileges.put(privilege, Boolean.FALSE); | ||
} |
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 would do this with an &&
as well, but I'm not fussed either way.
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.
Addressed this, Thank you.
} | ||
|
||
public boolean isAllowed(String privilege) { | ||
return (privileges.get(privilege)) != null ? privileges.get(privilege) : false; |
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.
Rather than doing 2 get
s on the map you could do:
return (privileges.get(privilege)) != null ? privileges.get(privilege) : false; | |
return Boolean.TRUE.equals(privileges.get(privilege)); |
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.
Addressed this, Thank you.
} | ||
|
||
public static class Builder { | ||
private String resource; |
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.
Per earlier comment, I'd make the resource
final and require it in the constructor.
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.
Addressed this, Thank you.
* @param forPrivileges list of index privileges | ||
* @return a map of resource to {@link ResourcePrivileges} | ||
*/ | ||
public ResourcesPrivileges getResourcePrivileges(List<String> forIndexPatterns, boolean allowRestrictedIndices, |
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 this method is going to exist, then it needs index
in its name somewhere, but I don't think it's really necessary is it?
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.
changed the method name to checkIndicesPrivileges
, Thank you.
* @param storedPrivileges stored {@link ApplicationPrivilegeDescriptor} for the application | ||
* @return an instance of {@link ResourcesPrivileges} | ||
*/ | ||
public ResourcesPrivileges getResourcePrivileges(final String applicationName, List<String> forResources, |
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.
Needs application
in its name.
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.
changed method name to checkApplicationResourcePrivileges
, Thank you.
- Correct the behavior for creating the has privileges API response. - Add tests - Add documentation with more explanation - Use of proper builders
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 left a few comments, mostly minor. Otherwise it LGTM, but please ensure @tvernum approves prior to merging.
@@ -33,6 +34,10 @@ public ClusterPrivilege privilege() { | |||
|
|||
public abstract boolean check(String action, TransportRequest request); | |||
|
|||
public boolean check(ClusterPrivilege clusterPrivilege) { |
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 do not think this should be named check. The method name is ambiguous
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.
Changed it to grants
, Thanks.
@@ -44,18 +47,18 @@ public IndicesAccessControl authorize(String action, Set<String> requestedIndice | |||
} | |||
|
|||
/** | |||
* @return A predicate that will match all the indices that this role and | |||
* the scoped by role has the privilege for executing the given action on. | |||
* @return A predicate that will match all the indices that this role and the scoped by role has the privilege for executing the given |
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 still mentions scoped by; I think we should change the wording to be more along the lines of limiting
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.
Done, Thank you.
|
||
public static class Builder { | ||
private boolean allowAll = true; | ||
private Map<String, ResourcePrivileges.Builder> resourceToResourcePrivilegesBuilder = new LinkedHashMap<>(); |
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.
make this final
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.
Done, Thank you.
|
||
public static class Builder { | ||
private final String resource; | ||
private Map<String, Boolean> privileges = new HashMap<>(); |
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.
make this final
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.
Done, Thank you.
@elasticmachine -- run elasticsearch-ci/oss-distro-docs |
@elasticmachine |
With the introduction of
LimitedRole
for API keys, the effectivepermissions are constrained to the permissions defined by the
limit by role.
This impacts the way we provide a response for Has Privileges API.
This change introduces a few helper methods on
Role
object whichcan be used to get resources privileges (index/application).
The methods are overridden in
LimitedRole
to take an intersectionof the privileges and return the result.
The
LimitedRole
changes also impact Get privileges API but wewould not be handling that case. The API is primarily aimed at
internal(Kibana) usage, the API is not supposed to be used for
authorization decisions and has been documented as such.