-
Notifications
You must be signed in to change notification settings - Fork 25.6k
Operator/role mapping #89667
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
Operator/role mapping #89667
Conversation
|
Pinging @elastic/es-core-infra (Team:Core/Infra) |
|
Hi @grcevski, I've created a changelog YAML for you. |
| * An extension of the {@link HandledTransportAction} class, which wraps the doExecute call with a check for clashes | ||
| * with the reserved cluster state. | ||
| */ | ||
| public abstract class ReservedStateAwareHandledTransportAction<Request extends ActionRequest, Response extends ActionResponse> extends |
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.
Since the role mappings don't use TransportMasterNodeAction (which is cluster state specific) we add this small wrapper class to ensure the reserved state information is checked before allowing REST actions to create/update/delete role mappings.
| format("Failed to process request [%s] with errors: [%s]", request, String.join(", ", errors)) | ||
| ); | ||
| } | ||
| ReservedClusterStateService.validateForReservedState(state, handlerName.get(), modifiedKeys(request), request.toString()); |
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.
Since this check is common now, I moved it to ReservedClusterStateService to be reused.
|
|
||
| // package private for testing | ||
| boolean supportsImmutableState() { | ||
| boolean supportsReservedState() { |
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.
These are simple renames, which were somehow missed before. We did rename from immutable to reserved.
| * @param modified the set of modified keys by the related request | ||
| * @param request a string representation of the request for error reporting purposes | ||
| */ | ||
| public static void validateForReservedState(ClusterState state, String handlerName, Set<String> modified, String 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.
Moved here from TransportMasterNodeAction for reuse.
| return new ExpressionRoleMapping(name, rules, roles, roleTemplates, metadata, enabled); | ||
| } | ||
|
|
||
| public static PutRoleMappingRequest fromMapping(ExpressionRoleMapping mapping) { |
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.
Helper method used by the reserved handler and test to create the request from the parsed mappings.
| } | ||
|
|
||
| @SuppressWarnings("unchecked") | ||
| public Collection<PutRoleMappingRequest> prepare(Object input) { |
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.
We convert from expression role mappings to a request to satisfy the native store API contract.
| /** | ||
| * Mock Security Provider implementation for the {@link ReservedClusterStateHandlerProvider} service interface | ||
| */ | ||
| public class LocalReservedSecurityStateHandlerProvider implements ReservedClusterStateHandlerProvider { |
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.
Mock version of the SPI handler provider so I could test properly with integration tests.
|
@elasticsearchmachine run elasticsearch-ci/part-1 |
ywangd
left a comment
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 is looking great. I don't think I'll find anything major. There are places that can use future refactoring. But I think the changes are plenty already for this PR. So we can wait.
Since there are many changes, I just need some more time to double check and read through the tests. But I'd like to make it clear that I am progressing on it by providing some minor optional feedback.
I also have a question: Will there only be a single file-state-update task to run at a time, i.e. it is not possible that two threads observed file changes and start parallel updating?
| return NAME; | ||
| } | ||
|
|
||
| public Collection<PutRoleMappingRequest> prepare(List<ExpressionRoleMapping> roleMappings) { |
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 this method be private?
...main/java/org/elasticsearch/xpack/security/action/rolemapping/ReservedRoleMappingAction.java
Outdated
Show resolved
Hide resolved
| return new TransformState(prevState.state(), prevState.keys(), ((l) -> nonStateTransform(requests, prevState, l))); | ||
| } | ||
|
|
||
| private Void nonStateTransform( |
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.
Nit: Is there a reason to use Void instead of void? I think using void is simpler because there is no need to return null in the code?
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.
100% I don't remember now why I made it Void, must've been an iteration I had when the consumer was a function.
|
I think we could use a few more tests. For example, making sure |
File based settings doesn't allow more than one update to run at the same time. So while no synchronization exists in the reserved state service, the file based settings service uses a latch to wait on until all async actions inside the reserved state service to finish, before it checks for new file updates. While an update is going on on certain file contents, the JDK watcher service can queue new file update changes, which will get processed one by one after the previous in-flight update finishes. We have a test for some of this logic in FileSettingsServiceTest.testStopWorksInMiddleOfProcessing and testStopWorksIfProcessingDidntReturnYet. |
Co-authored-by: Yang Wang <[email protected]>
Some of this is tested in RoleMappingFileSettingsIT.assertRoleMappingsSaveOK. At the end the method checks that a REST call cannot update any of the reserved role mappings, but this is a good call-out, I'll add explicit tests for validation of the keys in TransportPutRoleMappingAction, similarly as I've done for TransportPutLifecycleActionTests. I also have tests that perform the validation on TransportMasterNodeAction (TransportMasterNodeActionTests.testRejectImmutableConflictClusterStateUpdate), but not on the ReservedStateAwareHandledTransportAction which I introduced in the refactor. I'll add tests here too, thanks for bringing it up. |
ywangd
left a comment
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.
LGTM. Thanks for the iterations. I appreciate your patience and dedication to get this across the line. I learnt a lot in the process.
It would be nice if we could have tests for following two scenarios:
- Role mappings pass validation but fail to write to index. A easy way to achieve this is to close the security index.
- Role mappings succeed, but final cluster state update fails. I am not sure how easy or hard to force cluster state failure at that point.
Happy for them to be follow-ups.
Also my previous experience with Java's WatchService is that it does not work if the file system is NFS. Maybe this is no longer the case? Or maybe this feature does not need work with NFS? Anyhow, even it is an issue, it is not part of this PR. I am bringing it up just in case.
...est/java/org/elasticsearch/action/support/ReservedStateAwareHandledTransportActionTests.java
Outdated
Show resolved
Hide resolved
| "roles": [ "kibana_user" ], | ||
| "rules": { "field": { "username": "*" } }, | ||
| "metadata": { | ||
| "uuid" : "b9a59ba9-6b92-4be2-bb8d-02bb270cb3a7" |
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 add a reserved metadata key/value here, e.g. "_foo": "something"?
...main/java/org/elasticsearch/xpack/security/action/rolemapping/ReservedRoleMappingAction.java
Outdated
Show resolved
Hide resolved
| writeJSONFile(internalCluster().getMasterName(), emptyJSON); | ||
| boolean awaitSuccessful = savedClusterState.v1().await(20, TimeUnit.SECONDS); | ||
| assertTrue(awaitSuccessful); |
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.
Once the empty JSON file is processed, do the role mappings get deleted? If so, I think we should assert the role mappings are now gone and nothing is reserved.
Co-authored-by: Yang Wang <[email protected]>
Co-authored-by: Yang Wang <[email protected]>
…arch into operator/role_mapping
Good idea, I'm going to make a test for 1. I think I can test 2. with some mocked methods, but I guess in that case we are simply testing the flaw in our atomicity across the two stores. Perhaps we can add a test here if we ever fix this situation in the future.
Thanks for this info, I hadn't tested with an NFS file system. I think it's not part of the requirements, but we should definitely document is as a limitation. I'll follow-up with an investigation here. |
|
Thanks for all the help Yang! |
This PR adds support for /_security/role_mapping for file based settings. We create similar reserved cluster state information about the role mappings, even though they are not part of the cluster state.
Since the role_mappings are not saved in the cluster state, we needed to create separate reserved state action wrapper for any non-cluster state updates.
Relates to #89183