Skip to content

Conversation

DarshitChanpura
Copy link
Member

@DarshitChanpura DarshitChanpura commented Jun 17, 2025

Description

This PR adds a new privilege evaluator for evaluating access to a resource. #5281 introduced a way for plugin offload sharing and access evaluation to security plugin but that was done by requiring plugins to call verifyAccess method on their end. This leaves room for error. This new evaluator will filter all resource access requests through SecurityFilter class without requiring plugins to explicitly call verifyAccess method. It also adds support for access-levels instead of just the default one declared in the previous PR.

Notes:

  1. Removes verifyAccess from the client as plugin no longer have to explicitly call the method to check user access.
  2. Sharing model will be in full-effect. i.e. if a user has full access to a resource they can update, manage access or delete it.
  3. Doesn't consider hierarchical access. It will be added in future PRs.

Issues Resolved

#5442

Testing

  • Automated Tests

Check List

  • New functionality includes testing
  • New functionality has been documented
    - [ ] New Roles/Permissions have a corresponding security dashboards plugin PR
    - [ ] API changes companion pull request created
  • Commits are signed per the DCO using --signoff

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

@DarshitChanpura DarshitChanpura added the resource-permissions Label to track all items related to resource permissions label Jun 17, 2025
@DarshitChanpura DarshitChanpura force-pushed the standalone-resource-authz branch from 8c7eb57 to f636120 Compare June 18, 2025 17:40
Signed-off-by: Darshit Chanpura <[email protected]>
Signed-off-by: Darshit Chanpura <[email protected]>
Copy link

codecov bot commented Jun 18, 2025

Codecov Report

❌ Patch coverage is 78.01047% with 84 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.95%. Comparing base (1c2792a) to head (619a778).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
...org/opensearch/security/filter/SecurityFilter.java 56.16% 29 Missing and 3 partials ⚠️
...ecurity/resources/ResourceSharingIndexHandler.java 73.91% 16 Missing and 2 partials ⚠️
...h/security/privileges/ResourceAccessEvaluator.java 68.96% 8 Missing and 1 partial ⚠️
...arch/security/resources/ResourceAccessHandler.java 81.57% 6 Missing and 1 partial ⚠️
...ecurity/spi/resources/sharing/ResourceSharing.java 86.36% 2 Missing and 1 partial ⚠️
...tions/rest/revoke/RevokeResourceAccessRequest.java 71.42% 2 Missing ⚠️
...ource/actions/rest/share/ShareResourceRequest.java 71.42% 2 Missing ⚠️
...va/org/opensearch/security/auth/RolesInjector.java 75.00% 0 Missing and 2 partials ⚠️
...rch/security/transport/SecurityRequestHandler.java 86.66% 0 Missing and 2 partials ⚠️
...rce/actions/rest/create/CreateResourceRequest.java 50.00% 1 Missing ⚠️
... and 6 more
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #5408      +/-   ##
==========================================
+ Coverage   72.85%   72.95%   +0.09%     
==========================================
  Files         400      399       -1     
  Lines       24851    24924      +73     
  Branches     3782     3803      +21     
==========================================
+ Hits        18105    18183      +78     
+ Misses       4898     4896       -2     
+ Partials     1848     1845       -3     
Files with missing lines Coverage Δ
...org/opensearch/sample/SampleResourceExtension.java 100.00% <100.00%> (ø)
...va/org/opensearch/sample/SampleResourcePlugin.java 96.00% <100.00%> (-0.78%) ⬇️
...rce/actions/rest/create/UpdateResourceRequest.java 56.25% <100.00%> (+6.25%) ⬆️
...rce/actions/rest/delete/DeleteResourceRequest.java 58.33% <100.00%> (+8.33%) ⬆️
.../resource/actions/rest/get/GetResourceRequest.java 58.33% <100.00%> (+8.33%) ⬆️
...ce/actions/rest/share/ShareResourceRestAction.java 87.50% <100.00%> (+4.16%) ⬆️
...tions/transport/DeleteResourceTransportAction.java 66.66% <100.00%> (-2.78%) ⬇️
.../actions/transport/GetResourceTransportAction.java 88.88% <100.00%> (-4.96%) ⬇️
...transport/RevokeResourceAccessTransportAction.java 100.00% <100.00%> (ø)
...tions/transport/UpdateResourceTransportAction.java 77.77% <100.00%> (-2.72%) ⬇️
... and 23 more

... and 9 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Signed-off-by: Darshit Chanpura <[email protected]>
@DarshitChanpura DarshitChanpura force-pushed the standalone-resource-authz branch from 70f09f1 to bc312cc Compare July 30, 2025 22:40
cwperks
cwperks previously approved these changes Aug 1, 2025
Copy link
Collaborator

@nibix nibix left a comment

Choose a reason for hiding this comment

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

The async handling for resource privilege evaluation looks good! 👍

I am a bit surprised by the additional ThreadContext header introduced in this PR. It feels a bit unrelated. This will be enabled independent of the feature flag, correct? As this will significantly increase the request size for each request, this is a bit critical, IMHO.

nibix
nibix previously approved these changes Aug 4, 2025
Copy link
Collaborator

@nibix nibix left a comment

Choose a reason for hiding this comment

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

Thank you!

Approving. Still, I think we need to put a bit more thought into the serialization handling of the UserSubject; IMHO, it has quite a bit potential for inconsistencies at the moment:

String authUsrHdr = getThreadContext().getHeader(ConfigConstants.OPENDISTRO_SECURITY_AUTHENTICATED_USER_HEADER);
String shouldUseUserHeader = getThreadContext().getHeader(ConfigConstants.OPENDISTRO_SECURITY_USER_SAME_AS_SUBJECT_HEADER);
String userHeader = getThreadContext().getHeader(ConfigConstants.OPENDISTRO_SECURITY_USER_HEADER);
User user = null;
// restore a persistent user-subject from subject header
if (getThreadContext().getPersistent(ConfigConstants.OPENDISTRO_SECURITY_AUTHENTICATED_USER) == null) {
// when auth subject user is same request user.
if (Boolean.parseBoolean(shouldUseUserHeader) && userHeader != null) {
user = this.userFactory.fromSerializedBase64(userHeader);
getThreadContext().putPersistent(
ConfigConstants.OPENDISTRO_SECURITY_AUTHENTICATED_USER,
new UserSubjectImpl(getThreadPool(), user)
);
} else if (authUsrHdr != null) {
User authUser = this.userFactory.fromSerializedBase64(authUsrHdr);
getThreadContext().putPersistent(
ConfigConstants.OPENDISTRO_SECURITY_AUTHENTICATED_USER,
new UserSubjectImpl(getThreadPool(), authUser)
);
}
}
final String injectedRolesHeader = getThreadContext().getHeader(ConfigConstants.OPENDISTRO_SECURITY_INJECTED_ROLES_HEADER);
final String injectedUserHeader = getThreadContext().getHeader(ConfigConstants.OPENDISTRO_SECURITY_INJECTED_USER_HEADER);
if (Strings.isNullOrEmpty(userHeader)) {
// Keeping role injection with higher priority as plugins under OpenSearch will be using this
// on transport layer
if (!Strings.isNullOrEmpty(injectedRolesHeader)) {
getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_INJECTED_ROLES, injectedRolesHeader);
} else if (!Strings.isNullOrEmpty(injectedUserHeader)) {
getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_INJECTED_USER, injectedUserHeader);
}
} else {
user = user != null ? user : this.userFactory.fromSerializedBase64(userHeader);
getThreadContext().putTransient(
ConfigConstants.OPENDISTRO_SECURITY_USER,
user
);
}
String originalRemoteAddress = getThreadContext().getHeader(ConfigConstants.OPENDISTRO_SECURITY_REMOTE_ADDRESS_HEADER);
if (!Strings.isNullOrEmpty(originalRemoteAddress)) {
getThreadContext().putTransient(
ConfigConstants.OPENDISTRO_SECURITY_REMOTE_ADDRESS,
new TransportAddress((InetSocketAddress) Base64Helper.deserializeObject(originalRemoteAddress))
);
} else {
getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_REMOTE_ADDRESS, request.remoteAddress());
}
final String rolesValidation = getThreadContext().getHeader(
ConfigConstants.OPENDISTRO_SECURITY_INJECTED_ROLES_VALIDATION_HEADER
);
if (!Strings.isNullOrEmpty(rolesValidation)) {
getThreadContext().putTransient(ConfigConstants.OPENDISTRO_SECURITY_INJECTED_ROLES_VALIDATION, rolesValidation);
}

But as this is not actually the central point of this PR, this can and should be handled separately.

Signed-off-by: Darshit Chanpura <[email protected]>
@cwperks cwperks merged commit 11c71e7 into opensearch-project:main Aug 4, 2025
122 of 123 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

resource-permissions Label to track all items related to resource permissions v3.2.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants