-
Notifications
You must be signed in to change notification settings - Fork 24.8k
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
Introduce Application Privileges to Roles #30164
Introduce Application Privileges to Roles #30164
Conversation
Adds - CRUD actions for defining application level privileges - application privileges in role definitions - application privileges in the has_privileges API
Pinging @elastic/es-security |
This is awesome, thanks so much @tvernum. I just started integration it with our stuff, and I'll let you know how it progresses. I noticed that users with the |
Thanks for the reminder, I had forgotten about that. |
Also, in the |
You might be planning to explain this in the docs, but would you mind explaining the role that the
but then when I went to check the _has_privileges with
it was returning false. I noticed that I probably should be using the |
Apologies for all the questions, does Elasticsearch do some type of caching with the application privileges? I've noticed a few situations where things were behaving weirdly and not authorizing users as I expected where if I restarted Elasticsearch and ran the steps again, it's begin working again. It might just be a "user error" as well. |
We could, but I've tried to mimic the
There isn't supposed to be anything to explain, and I don't understand why you saw the behaviour you did.
There shouldn't be any. There's caching on roles, but I pulled out what I think we the last piece of caching on privielges because it was broken. |
I haven't been able to reproduce this. I suspect it's related to whatever caching issue you saw (whether real or user-error), but if you can still reproduce it let me know. My test case is:
Role
User
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 left some initial feedback. I haven't made it through the PR yet but wanted to provide what I have so far.
} | ||
|
||
public DeletePrivilegesRequest(String application, String[] privileges) { | ||
application(application); |
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.
can we use this.application = application
and this.privileges = privileges
? I find this much easier to follow than going to a method to see that this is all the method does.
this(client, DeletePrivilegesAction.INSTANCE); | ||
} | ||
|
||
public DeletePrivilegesRequestBuilder(ElasticsearchClient client, DeletePrivilegesAction action) { |
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 don't think we need this constructor and can just use the one above and call super with a instance of the request
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.
Unfortunately need it in order to implement DeletePrivilegesAction.newRequestBuilder
, but I've made it package protected.
} | ||
|
||
public DeletePrivilegesResponse(Collection<String> found) { | ||
this.found = new HashSet<>(found); |
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.
lets wrap in unmodifiable set here
} | ||
|
||
public Set<String> found() { | ||
return Collections.unmodifiableSet(this.found); |
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 we wrap in unmodifiable set in the constructor and serialization, this can just be return found;
public void readFrom(StreamInput in) throws IOException { | ||
super.readFrom(in); | ||
final int size = in.readVInt(); | ||
found = new HashSet<>(size); |
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.
Lets create a local value, add all of the values and then assign found to an unmodifiable set
return new PermissionEntry(k, Automatons.unionAndMinimize(Arrays.asList(existing.resources, patterns))); | ||
} | ||
})); | ||
this.permissions = new ArrayList<>(permissionsByPrivilege.values()); |
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 an unmodifiable list?
public boolean grants(ApplicationPrivilege other, String resource) { | ||
Automaton resourceAutomaton = Automatons.patterns(resource); | ||
final boolean matched = permissions.stream().anyMatch(e -> e.grants(other, resourceAutomaton)); | ||
logger.debug("Permission [{}] {} grant [{} , {}]", this, matched ? "does" : "does not", other, 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.
this feels like a trace level log to me
this.permissions = new ArrayList<>(permissionsByPrivilege.values()); | ||
} | ||
|
||
public boolean grants(ApplicationPrivilege other, 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.
Can you add javadocs?
} | ||
|
||
public ApplicationPrivilege(String application, String privilegeName, String... patterns) { | ||
this(application, Sets.newHashSet(privilegeName), patterns, Collections.emptyMap(), true); |
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.
use Collections.singleton here
this(application, Sets.newHashSet(privilegeName), patterns, Collections.emptyMap(), true); | ||
} | ||
|
||
private ApplicationPrivilege(String application, Set<String> name, Collection<String> patterns, Map<String, Object> metadata, |
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.
do we need this constructor?
@tvernum you were right that it appears to be a caching issue. If I perform the following steps to create the privileges, and a role/user with those privileges, and then update the privileges, the _has_privileges API returns false until I clear the cache for that role:
^^ this returns
^^ this returns Is this expected behavior? We're planning on occasionally updating the privileges map, and having to clear the cache of every role that has those privileges is rather cumbersome. |
It wasn't something I predicted, but it's consistent with some other caches in security. Updates to roles are live because the cached link between Users and Roles is simply by name, so if the role changes in the cache the User will see the new role on next request. But if you change a role-mapping so that LDAP user "bob" is supposed to now have "kibana_user", that won't be effective until the cache for "bob" is cleared.
You can clear the cache for all roles in 1 API call, though obviously that's a pretty blunt instrument. I'll look at options, but my gut feel is that I'd like to update the clear roles API to be able to accept an application name so you can clear all roles that have "kibana" privileges once you've finished adding/deleting/updating privileges. |
@kobelb How often does this happen? We can change the clear-cache API to ben more granular, but if the updates are infrequent, I think it would be simpler just to clear the whole cache when you update. |
@tvernum will a more granular control of the cache have any big impact on the performance of the API? |
@tvernum the current plan is to execute the Privileges In an ideal configuration, this would only happen once for every instance of Kibana for a long period of time; however, in an unideal situation or one where new instances were being provisioned frequently, it could happen somewhat frequently. Unfortunately, it depends... |
@jaymode @albertzaharovits @albertzaharovits I addressed your comment on the JSON input, but I'm going to hold off on your two |
@tvernum I just came across an interesting behavior, when I create a privilege with a capital letter in it, then the |
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 minor comments, otherwise I am good with merging this to the feature branch and moving forward.
@@ -969,6 +970,16 @@ public void writeList(List<? extends Writeable> list) throws IOException { | |||
} | |||
} | |||
|
|||
/** | |||
* Writes a list of generic objects via a {@link Writer} |
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.
s/list/collection
assertThat(copy.privileges(), Matchers.equalTo(original.privileges())); | ||
} | ||
|
||
private <T> T[] randomArray(int max, IntFunction<T[]> arrayConstructor, Supplier<T> valueConstructor) { |
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.
lets put this into ESTestCase
with javadocs and remove the duplication
This comment has been minimized.
This comment has been minimized.
I've tracked down the cause of this. I have a fix for it in a followup PR. |
This commit introduces "Application Privileges" (aka custom privileges) to the X-Pack security model.
Application Privileges are managed within Elasticsearch, and can be tested with the
_has_privileges
API, but do not grant access to any actions or resources within Elasticsearch.Their purpose is to allow applications outside of Elasticsearch to represent and store their own privileges model within Elasticsearch roles.
Specifically, this adds
Closes: #29820