diff --git a/CHANGELOG.md b/CHANGELOG.md index 5250a3c109..05454dc1c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,3 +8,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Maintenance - Bump `com.nimbusds:nimbus-jose-jwt:9.48` from 9.48 to 10.0.2 ([#5480](https://github.com/opensearch-project/security/pull/5480)) - Bump `checkstyle` from 10.3.3 to 10.26.1 ([#5480](https://github.com/opensearch-project/security/pull/5480)) +- Add tenancy access info to serialized user in threadcontext ([#5519](https://github.com/opensearch-project/security/pull/5519)) diff --git a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluatorImpl.java b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluatorImpl.java index 3947de6942..59f3095386 100644 --- a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluatorImpl.java +++ b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluatorImpl.java @@ -128,6 +128,11 @@ public class PrivilegesEvaluatorImpl implements PrivilegesEvaluator { ); private static final WildcardMatcher ACTION_MATCHER = WildcardMatcher.from("indices:data/read/*search*"); + private static final String USER_TENANT = "__user__"; + private static final String GLOBAL_TENANT = "global_tenant"; + private static final String READ_ACCESS = "READ"; + private static final String WRITE_ACCESS = "WRITE"; + private static final String NO_ACCESS = "NONE"; private static final IndicesOptions ALLOW_EMPTY = IndicesOptions.fromOptions(true, true, false, false); @@ -273,7 +278,7 @@ public boolean isInitialized() { return configModel != null && dcm != null && actionPrivileges.get() != null; } - private void setUserInfoInThreadContext(User user) { + private void setUserInfoInThreadContext(User user, Set mappedRoles) { if (threadContext.getTransient(OPENDISTRO_SECURITY_USER_INFO_THREAD_CONTEXT) == null) { StringJoiner joiner = new StringJoiner("|"); // Escape any pipe characters in the values before joining @@ -282,9 +287,10 @@ private void setUserInfoInThreadContext(User user) { joiner.add(escapePipe(String.join(",", user.getSecurityRoles()))); String requestedTenant = user.getRequestedTenant(); - if (!Strings.isNullOrEmpty(requestedTenant)) { - joiner.add(escapePipe(requestedTenant)); - } + joiner.add(requestedTenant); + String tenantAccessToCheck = getTenancyAccess(requestedTenant, mapTenants(user, mappedRoles)); + joiner.add(tenantAccessToCheck); + log.debug(joiner); threadContext.putTransient(OPENDISTRO_SECURITY_USER_INFO_THREAD_CONTEXT, joiner.toString()); } } @@ -310,6 +316,19 @@ public PrivilegesEvaluationContext createContext( return new PrivilegesEvaluationContext(user, mappedRoles, action0, request, task, irr, resolver, clusterStateSupplier); } + private String getTenancyAccess(String requestedTenant, Map tenancyAccessMap) { + final String tenant = Strings.isNullOrEmpty(requestedTenant) ? GLOBAL_TENANT : requestedTenant; + if (tenant.equals(USER_TENANT)) { + return WRITE_ACCESS; + } else { + if (tenancyAccessMap == null || !tenancyAccessMap.containsKey(tenant)) { + return NO_ACCESS; + } else { + return tenancyAccessMap.get(tenant) ? WRITE_ACCESS : READ_ACCESS; + } + } + } + public PrivilegesEvaluatorResponse evaluate(PrivilegesEvaluationContext context) { if (!isInitialized()) { @@ -353,7 +372,7 @@ public PrivilegesEvaluatorResponse evaluate(PrivilegesEvaluationContext context) // Add the security roles for this user so that they can be used for DLS parameter substitution. user.addSecurityRoles(mappedRoles); - setUserInfoInThreadContext(user); + setUserInfoInThreadContext(user, mappedRoles); final boolean isDebugEnabled = log.isDebugEnabled(); if (isDebugEnabled) { diff --git a/src/main/java/org/opensearch/security/privileges/legacy/PrivilegesEvaluatorImpl.java b/src/main/java/org/opensearch/security/privileges/legacy/PrivilegesEvaluatorImpl.java index f5c2f48be5..c9f575ce1f 100644 --- a/src/main/java/org/opensearch/security/privileges/legacy/PrivilegesEvaluatorImpl.java +++ b/src/main/java/org/opensearch/security/privileges/legacy/PrivilegesEvaluatorImpl.java @@ -126,6 +126,11 @@ public class PrivilegesEvaluatorImpl implements PrivilegesEvaluator { ); private static final WildcardMatcher ACTION_MATCHER = WildcardMatcher.from("indices:data/read/*search*"); + private static final String USER_TENANT = "__user__"; + private static final String GLOBAL_TENANT = "global_tenant"; + private static final String READ_ACCESS = "READ"; + private static final String WRITE_ACCESS = "WRITE"; + private static final String NO_ACCESS = "NONE"; private static final IndicesOptions ALLOW_EMPTY = IndicesOptions.fromOptions(true, true, false, false); @@ -209,7 +214,7 @@ public SecurityRoles getSecurityRoles(User user, Set roles) { // Add the security roles for this user so that they can be used for DLS parameter substitution. user.addSecurityRoles(roles); - setUserInfoInThreadContext(user); + setUserInfoInThreadContext(user, roles); } return securityRoles; @@ -243,7 +248,7 @@ public boolean isInitialized() { return configModel != null && configModel.getSecurityRoles() != null && dcm != null; } - private void setUserInfoInThreadContext(User user) { + private void setUserInfoInThreadContext(User user, Set mappedRoles) { if (threadContext.getTransient(OPENDISTRO_SECURITY_USER_INFO_THREAD_CONTEXT) == null) { StringJoiner joiner = new StringJoiner("|"); // Escape any pipe characters in the values before joining @@ -252,9 +257,10 @@ private void setUserInfoInThreadContext(User user) { joiner.add(escapePipe(String.join(",", user.getSecurityRoles()))); String requestedTenant = user.getRequestedTenant(); - if (!Strings.isNullOrEmpty(requestedTenant)) { - joiner.add(escapePipe(requestedTenant)); - } + joiner.add(requestedTenant); + String tenantAccessToCheck = getTenancyAccess(requestedTenant, mapTenants(user, mappedRoles)); + joiner.add(tenantAccessToCheck); + log.debug(joiner); threadContext.putTransient(OPENDISTRO_SECURITY_USER_INFO_THREAD_CONTEXT, joiner.toString()); } } @@ -280,6 +286,19 @@ public PrivilegesEvaluationContext createContext( return new PrivilegesEvaluationContext(user, mappedRoles, action0, request, task, irr, resolver, () -> clusterService.state()); } + private String getTenancyAccess(String requestedTenant, Map tenancyAccessMap) { + final String tenant = Strings.isNullOrEmpty(requestedTenant) ? GLOBAL_TENANT : requestedTenant; + if (tenant.equals(USER_TENANT)) { + return WRITE_ACCESS; + } else { + if (tenancyAccessMap == null || !tenancyAccessMap.containsKey(tenant)) { + return NO_ACCESS; + } else { + return tenancyAccessMap.get(tenant) ? WRITE_ACCESS : READ_ACCESS; + } + } + } + public PrivilegesEvaluatorResponse evaluate(PrivilegesEvaluationContext context) { if (!isInitialized()) {