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

Add BulkPutRoles API #109339

Merged
merged 33 commits into from
Jul 2, 2024
Merged

Add BulkPutRoles API #109339

merged 33 commits into from
Jul 2, 2024

Conversation

jfreden
Copy link
Contributor

@jfreden jfreden commented Jun 4, 2024

This PR adds a new POST _security/role API that can be used to bulk insert roles.

Example

Request

POST _security/role
{
    "roles": {
        "test": {"cluster": ["all"],"indices": [{"names": ["test_index1"],"privileges": ["all"]}],"applications": [],"run_as": []},
        "test2": {"cluster": ["all"],"indices": [{"names": ["test_index1"],"privileges": ["read"]}],"applications": [],"run_as": []},
        "test3": {"cluster": ["all"],"indices": [{"names": ["test_index1"],"privileges": ["all"]}],"applications": [],"run_as": []},
        "test4": {"cluster": ["all"],"indices": [{"names": ["test_index1"],"privileges": ["write"]}],"applications": [],"run_as": []},
        "test5": {"cluster": ["all"],"indices": [{"names": ["test_index1"],"privileges": ["all"]}],"applications": [],"run_as": []}
    }
}

Response

{
    "created": [
        "test",
        "test2",
        "test3",
        "test4",
        "test5"
    ]
}

Under the hood it uses a BulkRequest with a collection of UpdateRequest with doc_as_upsert to allow for noops against the Lucene index when no changes are detected in the payload.

This doesn't yet support serveless (set to internal) since a separate request builder need to be added to build put role requests that satisfy custom roles. This should ideally use the new manage_roles privilege when available and should no be enabled in serverless until then.

Ater Merge

  1. Add new manage_roles privilege to allow us to use this in serverless and enhance security.
  2. Create a separate serverless PR to handle validation for custom roles
  3. Make this API available in serverless
  4. Add new API spec to https://github.com/elastic/elasticsearch-specification

return new BulkPutRoleRequestBuilder(client);
}
}
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is needed to allow us to inject another builder through SPI when running in serverless to enforce custom roles validation for the bulk api.

Copy link
Contributor

Choose a reason for hiding this comment

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

My suggestion is to make this PR simpler and not think about validation for serverless roles. Your call.

}

public RoleDescriptor parse(String name, XContentParser parser, boolean validate) throws IOException {
if (validate) {
Copy link
Contributor Author

@jfreden jfreden Jun 12, 2024

Choose a reason for hiding this comment

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

We want to postpone validation until we handle the rest of the request so we can merge actual results with validation errors and produce a single stream of results. The boolean here provides a way to skip validation that will be done later.

}
}

private Exception validateRequest(final PutRoleRequest request) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is now handled in the roles store instead, so the logic can be shared between bulk and single inserts. The benefit of keeping it in the roles store is that we can then merge the failed validation items with successful inserts.

@@ -92,109 +82,6 @@ protected NamedXContentRegistry xContentRegistry() {
);
}

public void testReservedRole() {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

All these tests have been moved to NativeRolesStoreTests.

@jfreden jfreden marked this pull request as ready for review June 12, 2024 11:58
@jfreden jfreden added the :Security/Security Security issues without another label label Jun 12, 2024
@elasticsearchmachine elasticsearchmachine added the Team:Security Meta label for security team label Jun 12, 2024
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-security (Team:Security)

@elasticsearchmachine
Copy link
Collaborator

Hi @jfreden, I've created a changelog YAML for you.

@jfreden
Copy link
Contributor Author

jfreden commented Jun 12, 2024

This is trying to look like the bulk index api as much as possible. Open to modifying things if some of the naming is confusing.

@albertzaharovits albertzaharovits added the >docs General docs changes label Jun 13, 2024
@elasticsearchmachine elasticsearchmachine added the Team:Docs Meta label for docs team label Jun 13, 2024
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-docs (Team:Docs)

Copy link
Contributor

@shainaraskas shainaraskas left a comment

Choose a reason for hiding this comment

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

A couple of small notes from the docs side!

@albertzaharovits albertzaharovits self-requested a review July 2, 2024 06:43
Copy link
Contributor

@albertzaharovits albertzaharovits left a comment

Choose a reason for hiding this comment

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

LGTM
Only minor nits that are optional to address.
I think we should discuss (in a GH issue, or the team meeting) what to do when (bulk) creating roles succeeds but cache clearing fails. Can you raise an issue to track this discussion, please?

Comment on lines 34 to 38
try {
rolesStore.putRoles(request.getRefreshPolicy(), request.getRoles(), listener);
} catch (IOException e) {
listener.onFailure(e);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can you please make putRoles both throw an exception and take a listener parameter. It avoids code like this and should make the method itself clearer.

validationException = RoleDescriptorRequestValidator.validate(role, validationException);

if (reservedRoleNameChecker.isReserved(role.getName())) {
throw addValidationError("Role [" + role.getName() + "] is reserved and may not be used.", validationException);
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: maybe not throw here, looks dissimilar to the prevailing convention around here to return the validation exception.

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, this is to match the existing behaviour from when this lived in the TransportPutRoleAction, but I agree this is not very nice. I'm a little worried to change existing behaviour of the PutRole so I rather keep it as is for now. WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmmm, OK, lets keep the existing behavior.

Iterator<BulkItemResponse> bulkItemResponses = bulkResponse.iterator();
BulkPutRolesResponse.Builder bulkPutRolesResponseBuilder = new BulkPutRolesResponse.Builder();

roles.stream().map(RoleDescriptor::getName).map(roleName -> {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: can you please create a roleNames list outside the response handler here, to avoid keeping the list of RoleDescriptor around for the response for the names only? (also, I would assert that the list of role names contains unique elements).

Comment on lines +435 to +437
clearRoleCache(rolesToRefreshInCache.toArray(String[]::new), ActionListener.wrap(res -> {
listener.onResponse(bulkPutRolesResponseBuilder.build());
}, listener::onFailure), bulkResponse);
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 we should return the bulk response when the cache clear operation failed.
We can discuss separately, but please raise an issue to track 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.

Ah yes. Hmm that's a little tricky. I'll add an item to our weekly + a jira ticket to make sure I don't forget.

@jfreden
Copy link
Contributor Author

jfreden commented Jul 2, 2024

Thanks for the review @albertzaharovits !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
>docs General docs changes >enhancement :Security/Security Security issues without another label Team:Docs Meta label for docs team Team:Security Meta label for security team test-full-bwc Trigger full BWC version matrix tests test-update-serverless v8.15.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants