Skip to content
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

Merged

Conversation

bizybot
Copy link
Contributor

@bizybot bizybot commented Jan 24, 2019

With the 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 a response for Has Privileges API.
This change introduces a 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.

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.
@bizybot bizybot added >non-issue :Security/Authorization Roles, Privileges, DLS/FLS, RBAC/ABAC labels Jan 24, 2019
@bizybot bizybot requested review from tvernum and jaymode January 24, 2019 17:36
@elasticmachine
Copy link
Collaborator

Pinging @elastic/es-security

Copy link
Contributor

@tvernum tvernum left a 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 {
Copy link
Contributor

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.

Copy link
Contributor Author

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,
Copy link
Contributor

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.

Copy link
Contributor Author

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}
Copy link
Contributor

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)

Copy link
Contributor Author

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);
Copy link
Contributor

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.

Copy link
Contributor Author

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());
Copy link
Contributor

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?

Copy link
Contributor Author

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);
}
Copy link
Contributor

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.

Copy link
Contributor Author

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than doing 2 gets on the map you could do:

Suggested change
return (privileges.get(privilege)) != null ? privileges.get(privilege) : false;
return Boolean.TRUE.equals(privileges.get(privilege));

Copy link
Contributor Author

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;
Copy link
Contributor

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.

Copy link
Contributor Author

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,
Copy link
Contributor

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?

Copy link
Contributor Author

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,
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Yogesh Gaikwad added 2 commits January 25, 2019 22:49
- Correct the behavior for creating the has privileges API response.
- Add tests
- Add documentation with more explanation
- Use of proper builders
Copy link
Member

@jaymode jaymode left a 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) {
Copy link
Member

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

Copy link
Contributor Author

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
Copy link
Member

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

Copy link
Contributor Author

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<>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make this final

Copy link
Contributor Author

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<>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

make this final

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, Thank you.

@bizybot
Copy link
Contributor Author

bizybot commented Jan 29, 2019

@elasticmachine -- run elasticsearch-ci/oss-distro-docs

@bizybot
Copy link
Contributor Author

bizybot commented Jan 29, 2019

@elasticmachine
run elasticsearch-ci/1
run elasticsearch-ci/default-distro

@bizybot bizybot merged commit 382d6cb into elastic:security_api_keys Jan 30, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
>non-issue :Security/Authorization Roles, Privileges, DLS/FLS, RBAC/ABAC
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants