From 13262cd60ac37f91d83f96b8dba79a2984dcb107 Mon Sep 17 00:00:00 2001 From: Stephen Crawford Date: Mon, 12 Aug 2024 14:49:13 -0400 Subject: [PATCH 1/7] Expand RoleV7 to include Resources Signed-off-by: Stephen Crawford --- .../security/securityconf/ConfigModelV7.java | 130 ++++++++++++++++++ .../security/securityconf/impl/v7/RoleV7.java | 62 +++++++++ 2 files changed, 192 insertions(+) diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index 84902bba3e..fd7e982526 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -63,6 +63,7 @@ import org.opensearch.security.securityconf.impl.v7.RoleMappingsV7; import org.opensearch.security.securityconf.impl.v7.RoleV7; import org.opensearch.security.securityconf.impl.v7.RoleV7.Index; +import org.opensearch.security.securityconf.impl.v7.RoleV7.Resource; import org.opensearch.security.securityconf.impl.v7.TenantV7; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.support.WildcardMatcher; @@ -179,6 +180,16 @@ public SecurityRole call() throws Exception { } + for (final Resource permittedResourceAliases : securityRole.getValue().getResource_permissions()) { + + for (String resourcePattern : permittedResourceAliases.getResource_patterns()) { + ResourcePattern _resourcePattern = new ResourcePattern(resourcePattern); + _resourcePattern.addPerm(actionGroups.resolve(permittedResourceAliases.getAllowed_actions())); + + _securityRole.addResourcePattern(_resourcePattern); + } + } + return _securityRole.build(); } }); @@ -912,6 +923,125 @@ public Set getPerms() { }*/ + public static class ResourcePattern { + private final String resourcePattern; + private final Set perms = new HashSet<>(); + + public ResourcePattern(String resourcePattern) { + super(); + this.resourcePattern = Objects.requireNonNull(resourcePattern); + } + + public ResourcePattern addPerm(Set perms) { + if (perms != null) { + this.perms.addAll(perms); + } + return this; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((resourcePattern == null) ? 0 : resourcePattern.hashCode()); + result = prime * result + ((perms == null) ? 0 : perms.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + ResourcePattern other = (ResourcePattern) obj; + if (resourcePattern == null) { + if (other.resourcePattern != null) return false; + } else if (!resourcePattern.equals(other.resourcePattern)) return false; + if (perms == null) { + if (other.perms != null) return false; + } else if (!perms.equals(other.perms)) return false; + return true; + } + + @Override + public String toString() { + return System.lineSeparator() + + " resourcePattern=" + + resourcePattern + + System.lineSeparator() + + " perms=" + + perms; + } + + public String getUnresolvedResourcePattern(User user) { + return UserAttributes.replaceProperties(resourcePattern, user); + } + + /** Finds the resources accessible to the user and resolves them to concrete names */ + public Set concreteResourceNames(final User user, final ResourceNameExpressionResolver resolver, final ClusterService cs) { + return getResolvedResourcePattern(user, resolver, cs, false); + } + + /** Finds the resources accessible to the user and attempts to resolve them to names, also includes any unresolved names */ + public Set attemptResolveIndexNames(final User user, final ResourceNameExpressionResolver resolver, final ClusterService cs) { + return getResolvedResourcePattern(user, resolver, cs, true); + } + + + public Set getResolvedResourcePattern( + final User user, + final ResourceNameExpressionResolver resolver, + final ClusterService cs, + final boolean appendUnresolved + ) { + final String unresolved = getUnresolvedResourcePattern(user); + final ImmutableSet.Builder resolvedResources = new ImmutableSet.Builder<>(); + + final WildcardMatcher matcher = WildcardMatcher.from(unresolved); + boolean includeDataStreams = true; + if (!(matcher instanceof WildcardMatcher.Exact)) { + final String[] aliasesAndDataStreamsForPermittedPattern = cs.state() + .getMetadata() + .getResourcesLookup() + .entrySet() + .stream() + .filter(e -> (e.getValue().getType() == ALIAS) || (e.getValue().getType() == DATA_STREAM)) + .filter(e -> matcher.test(e.getKey())) + .map(e -> e.getKey()) + .toArray(String[]::new); + if (aliasesAndDataStreamsForPermittedPattern.length > 0) { + final String[] resolvedAliasesAndDataStreamResources = resolver.concreteResourceNames( + cs.state(), + ResourceOptions.lenientExpandOpen(), + includeDataStreams, + aliasesAndDataStreamsForPermittedPattern + ); + resolvedResources.addAll(Arrays.asList(resolvedAliasesAndDataStreamResources)); + } + } + + if (Strings.isNotBlank(unresolved)) { + final String[] resolvedResourcesFromPattern = resolver.concreteResourceNames( + cs.state(), + ResourceOptions.lenientExpandOpen(), + includeDataStreams, + unresolved + ); + resolvedResources.addAll(Arrays.asList(resolvedResourcesFromPattern)); + } + + if (appendUnresolved || resolvedResources.build().isEmpty()) { + resolvedResources.add(unresolved); + } + return resolvedResources.build(); + } + + public WildcardMatcher getPerms() { + return WildcardMatcher.from(perms); + } + + } + public static class Tenant { private final String tenant; private final boolean readWrite; diff --git a/src/main/java/org/opensearch/security/securityconf/impl/v7/RoleV7.java b/src/main/java/org/opensearch/security/securityconf/impl/v7/RoleV7.java index 229cae0e6f..73396f500b 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/v7/RoleV7.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/v7/RoleV7.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Map.Entry; @@ -51,6 +52,7 @@ public class RoleV7 implements Hideable, StaticDefinable { private List cluster_permissions = Collections.emptyList(); private List index_permissions = Collections.emptyList(); private List tenant_permissions = Collections.emptyList(); + private List resource_permissions = Collections.emptyList(); public RoleV7() { @@ -63,6 +65,7 @@ public RoleV7(RoleV6 roleV6) { this.cluster_permissions = roleV6.getCluster(); index_permissions = new ArrayList<>(); tenant_permissions = new ArrayList<>(); + resource_permissions = new ArrayList<>(); for (Entry v6i : roleV6.getIndices().entrySet()) { index_permissions.add(new Index(v6i.getKey(), v6i.getValue())); @@ -225,6 +228,57 @@ public String toString() { } + public static class Resource { + + private String uniqueId; + private List resource_patterns; + private Date lastModifiedAt; + private List allowed_actions = Collections.emptyList(); + + public Resource(String resourceName, List resourcePattern) { + super(); + uniqueId = resourceName; + lastModifiedAt = new Date(); + Set tmpActions = new HashSet<>(); + resource_patterns = resourcePattern; + allowed_actions = new ArrayList<>(tmpActions); + } + + public Resource() { + super(); + } + + public List getAllowed_actions() { + return allowed_actions; + } + + public void setAllowed_actions(List allowed_actions) { + lastModifiedAt = new Date(); + this.allowed_actions = allowed_actions; + } + + public List getResource_patterns() { + return resource_patterns; + } + + public void setResource_patterns(List resource_patterns) { + this.resource_patterns = resource_patterns; + } + + @Override + public String toString() { + return "Resource [uniqueId=" + + uniqueId + + ", lastModifiedAt=" + + lastModifiedAt + + ", resource_patterns=" + + resource_patterns + + ", allowed_actions=" + + allowed_actions + + "]"; + } + } + public boolean isHidden() { return hidden; } @@ -265,6 +319,14 @@ public void setTenant_permissions(List tenant_permissions) { this.tenant_permissions = tenant_permissions; } + public List getResource_permissions() { + return resource_permissions; + } + + public void setResource_permissions(List resource_permissions) { + this.resource_permissions = resource_permissions; + } + public boolean isReserved() { return reserved; } From fb40a6f6a176a7a8c095ed849886553e735072e5 Mon Sep 17 00:00:00 2001 From: Stephen Crawford Date: Mon, 12 Aug 2024 15:04:55 -0400 Subject: [PATCH 2/7] Expand RoleV7 to include Resources Signed-off-by: Stephen Crawford --- .../security/OpenSearchSecurityPlugin.java | 4 + .../privileges/ResourceAccessEvaluator.java | 104 ++++++++++++++++++ .../security/securityconf/SecurityRoles.java | 2 + 3 files changed, 110 insertions(+) create mode 100644 src/main/java/org/opensearch/security/privileges/ResourceAccessEvaluator.java diff --git a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java index 509b98f12e..202eae17d0 100644 --- a/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java +++ b/src/main/java/org/opensearch/security/OpenSearchSecurityPlugin.java @@ -167,6 +167,7 @@ import org.opensearch.security.identity.SecurityTokenManager; import org.opensearch.security.privileges.PrivilegesEvaluator; import org.opensearch.security.privileges.PrivilegesInterceptor; +import org.opensearch.security.privileges.ResourceAccessEvaluator; import org.opensearch.security.privileges.RestLayerPrivilegesEvaluator; import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.rest.DashboardsInfoAction; @@ -246,6 +247,7 @@ public final class OpenSearchSecurityPlugin extends OpenSearchSecuritySSLPlugin private volatile PrivilegesEvaluator evaluator; private volatile UserService userService; private volatile RestLayerPrivilegesEvaluator restLayerEvaluator; + private volatile ResourceAccessEvaluator resourceAccessEvaluator; private volatile ConfigurationRepository cr; private volatile AdminDNs adminDns; private volatile ClusterService cs; @@ -1139,6 +1141,8 @@ public Collection createComponents( restLayerEvaluator = new RestLayerPrivilegesEvaluator(clusterService, threadPool); + resourceAccessEvaluator = new ResourceAccessEvaluator(clusterService, threadPool); + securityRestHandler = new SecurityRestFilter( backendRegistry, restLayerEvaluator, diff --git a/src/main/java/org/opensearch/security/privileges/ResourceAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/ResourceAccessEvaluator.java new file mode 100644 index 0000000000..e9cc958a91 --- /dev/null +++ b/src/main/java/org/opensearch/security/privileges/ResourceAccessEvaluator.java @@ -0,0 +1,104 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + * + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.security.privileges; + +import java.util.Set; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import org.opensearch.OpenSearchSecurityException; +import org.opensearch.cluster.service.ClusterService; +import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.security.securityconf.ConfigModel; +import org.opensearch.security.securityconf.SecurityRoles; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.security.user.User; +import org.opensearch.threadpool.ThreadPool; + +import org.greenrobot.eventbus.Subscribe; + +public class ResourceAccessEvaluator { + protected final Logger log = LogManager.getLogger(this.getClass()); + private final ClusterService clusterService; + private ThreadContext threadContext; + private ConfigModel configModel; + + public ResourceAccessEvaluator(final ClusterService clusterService, final ThreadPool threadPool) { + this.clusterService = clusterService; + this.threadContext = threadPool.getThreadContext(); + } + + @Subscribe + public void onConfigModelChanged(final ConfigModel configModel) { + this.configModel = configModel; + } + + SecurityRoles getSecurityRoles(final Set roles) { + return configModel.getSecurityRoles().filter(roles); + } + + boolean isInitialized() { + return configModel != null && configModel.getSecurityRoles() != null; + } + + public PrivilegesEvaluatorResponse evaluate(final User user, final Set actions) { + if (!isInitialized()) { + throw new OpenSearchSecurityException("OpenSearch Security is not initialized."); + } + + final PrivilegesEvaluatorResponse presponse = new PrivilegesEvaluatorResponse(); + + final TransportAddress caller = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_REMOTE_ADDRESS); + + final Set mappedRoles = mapRoles(user, caller); + + presponse.resolvedSecurityRoles.addAll(mappedRoles); + final SecurityRoles securityRoles = getSecurityRoles(mappedRoles); + + final boolean isDebugEnabled = log.isDebugEnabled(); + if (isDebugEnabled) { + log.debug("Evaluate permissions for {} on {}", user, clusterService.localNode().getName()); + log.debug("Action: {}", actions); + log.debug("Mapped roles: {}", mappedRoles.toString()); + } + + for (final String action : actions) { + if (!securityRoles.impliesResourcePermission(action)) { + presponse.missingPrivileges.add(action); + presponse.allowed = false; + log.info( + "No permission match for {} [Action [{}]] [RolesChecked {}]. No permissions for {}", + user, + action, + securityRoles.getRoleNames(), + presponse.missingPrivileges + ); + } else { + if (isDebugEnabled) { + log.debug("Allowed because we have permissions for {}", actions); + } + presponse.allowed = true; + + // break the loop as we found the matching permission + break; + } + } + + return presponse; + } + + Set mapRoles(final User user, final TransportAddress caller) { + return this.configModel.mapSecurityRoles(user, caller); + } +} diff --git a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java index fb25e1a21f..0e1070c6bd 100644 --- a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java +++ b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java @@ -98,4 +98,6 @@ Set getAllPermittedIndicesForDashboards( SecurityRoles filter(Set roles); boolean isPermittedOnSystemIndex(String indexName); + + boolean impliesResourcePermission(String action0); } From 59df3dfd46d409162fcedc3f7f608ca17703b9bc Mon Sep 17 00:00:00 2001 From: Stephen Crawford Date: Mon, 12 Aug 2024 15:49:33 -0400 Subject: [PATCH 3/7] Expand RoleV7 to include Resources Signed-off-by: Stephen Crawford --- .../security/securityconf/ConfigModelV7.java | 135 +----------------- 1 file changed, 5 insertions(+), 130 deletions(-) diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index fd7e982526..0e6143187f 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -63,7 +63,6 @@ import org.opensearch.security.securityconf.impl.v7.RoleMappingsV7; import org.opensearch.security.securityconf.impl.v7.RoleV7; import org.opensearch.security.securityconf.impl.v7.RoleV7.Index; -import org.opensearch.security.securityconf.impl.v7.RoleV7.Resource; import org.opensearch.security.securityconf.impl.v7.TenantV7; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.support.WildcardMatcher; @@ -180,16 +179,6 @@ public SecurityRole call() throws Exception { } - for (final Resource permittedResourceAliases : securityRole.getValue().getResource_permissions()) { - - for (String resourcePattern : permittedResourceAliases.getResource_patterns()) { - ResourcePattern _resourcePattern = new ResourcePattern(resourcePattern); - _resourcePattern.addPerm(actionGroups.resolve(permittedResourceAliases.getAllowed_actions())); - - _securityRole.addResourcePattern(_resourcePattern); - } - } - return _securityRole.build(); } }); @@ -515,6 +504,11 @@ public boolean isPermittedOnSystemIndex(String indexName) { } return isPatternMatched && isPermitted; } + + @Override + public boolean impliesResourcePermission(String action0) { + return roles.stream().filter(r -> r.impliesClusterPermission(action0)).count() > 0; + } } public static class SecurityRole { @@ -923,125 +917,6 @@ public Set getPerms() { }*/ - public static class ResourcePattern { - private final String resourcePattern; - private final Set perms = new HashSet<>(); - - public ResourcePattern(String resourcePattern) { - super(); - this.resourcePattern = Objects.requireNonNull(resourcePattern); - } - - public ResourcePattern addPerm(Set perms) { - if (perms != null) { - this.perms.addAll(perms); - } - return this; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((resourcePattern == null) ? 0 : resourcePattern.hashCode()); - result = prime * result + ((perms == null) ? 0 : perms.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (getClass() != obj.getClass()) return false; - ResourcePattern other = (ResourcePattern) obj; - if (resourcePattern == null) { - if (other.resourcePattern != null) return false; - } else if (!resourcePattern.equals(other.resourcePattern)) return false; - if (perms == null) { - if (other.perms != null) return false; - } else if (!perms.equals(other.perms)) return false; - return true; - } - - @Override - public String toString() { - return System.lineSeparator() - + " resourcePattern=" - + resourcePattern - + System.lineSeparator() - + " perms=" - + perms; - } - - public String getUnresolvedResourcePattern(User user) { - return UserAttributes.replaceProperties(resourcePattern, user); - } - - /** Finds the resources accessible to the user and resolves them to concrete names */ - public Set concreteResourceNames(final User user, final ResourceNameExpressionResolver resolver, final ClusterService cs) { - return getResolvedResourcePattern(user, resolver, cs, false); - } - - /** Finds the resources accessible to the user and attempts to resolve them to names, also includes any unresolved names */ - public Set attemptResolveIndexNames(final User user, final ResourceNameExpressionResolver resolver, final ClusterService cs) { - return getResolvedResourcePattern(user, resolver, cs, true); - } - - - public Set getResolvedResourcePattern( - final User user, - final ResourceNameExpressionResolver resolver, - final ClusterService cs, - final boolean appendUnresolved - ) { - final String unresolved = getUnresolvedResourcePattern(user); - final ImmutableSet.Builder resolvedResources = new ImmutableSet.Builder<>(); - - final WildcardMatcher matcher = WildcardMatcher.from(unresolved); - boolean includeDataStreams = true; - if (!(matcher instanceof WildcardMatcher.Exact)) { - final String[] aliasesAndDataStreamsForPermittedPattern = cs.state() - .getMetadata() - .getResourcesLookup() - .entrySet() - .stream() - .filter(e -> (e.getValue().getType() == ALIAS) || (e.getValue().getType() == DATA_STREAM)) - .filter(e -> matcher.test(e.getKey())) - .map(e -> e.getKey()) - .toArray(String[]::new); - if (aliasesAndDataStreamsForPermittedPattern.length > 0) { - final String[] resolvedAliasesAndDataStreamResources = resolver.concreteResourceNames( - cs.state(), - ResourceOptions.lenientExpandOpen(), - includeDataStreams, - aliasesAndDataStreamsForPermittedPattern - ); - resolvedResources.addAll(Arrays.asList(resolvedAliasesAndDataStreamResources)); - } - } - - if (Strings.isNotBlank(unresolved)) { - final String[] resolvedResourcesFromPattern = resolver.concreteResourceNames( - cs.state(), - ResourceOptions.lenientExpandOpen(), - includeDataStreams, - unresolved - ); - resolvedResources.addAll(Arrays.asList(resolvedResourcesFromPattern)); - } - - if (appendUnresolved || resolvedResources.build().isEmpty()) { - resolvedResources.add(unresolved); - } - return resolvedResources.build(); - } - - public WildcardMatcher getPerms() { - return WildcardMatcher.from(perms); - } - - } - public static class Tenant { private final String tenant; private final boolean readWrite; From 12d8c8c24e1aefc02c35f88f1dadff0039a3106b Mon Sep 17 00:00:00 2001 From: Stephen Crawford Date: Mon, 12 Aug 2024 16:43:22 -0400 Subject: [PATCH 4/7] Expand RoleV7 to include Resources Signed-off-by: Stephen Crawford --- .../privileges/ResourceAccessEvaluator.java | 33 +++++++++++-------- .../security/securityconf/ConfigModelV7.java | 12 ++++--- .../security/securityconf/SecurityRoles.java | 3 +- 3 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/ResourceAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/ResourceAccessEvaluator.java index e9cc958a91..8f6e8ad59b 100644 --- a/src/main/java/org/opensearch/security/privileges/ResourceAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/ResourceAccessEvaluator.java @@ -11,19 +11,24 @@ package org.opensearch.security.privileges; +import java.util.List; import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.opensearch.OpenSearchSecurityException; +import org.opensearch.action.ActionRequest; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.common.transport.TransportAddress; +import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.securityconf.ConfigModel; import org.opensearch.security.securityconf.SecurityRoles; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.user.User; +import org.opensearch.tasks.Task; import org.opensearch.threadpool.ThreadPool; import org.greenrobot.eventbus.Subscribe; @@ -52,7 +57,11 @@ boolean isInitialized() { return configModel != null && configModel.getSecurityRoles() != null; } - public PrivilegesEvaluatorResponse evaluate(final User user, final Set actions) { + public PrivilegesEvaluatorResponse evaluate(final ActionRequest request, + final String action, + final SecurityRoles securityRoles, + final User user, + final ClusterService clusterService) { if (!isInitialized()) { throw new OpenSearchSecurityException("OpenSearch Security is not initialized."); } @@ -64,17 +73,22 @@ public PrivilegesEvaluatorResponse evaluate(final User user, final Set a final Set mappedRoles = mapRoles(user, caller); presponse.resolvedSecurityRoles.addAll(mappedRoles); - final SecurityRoles securityRoles = getSecurityRoles(mappedRoles); final boolean isDebugEnabled = log.isDebugEnabled(); if (isDebugEnabled) { log.debug("Evaluate permissions for {} on {}", user, clusterService.localNode().getName()); - log.debug("Action: {}", actions); + log.debug("Action: {}", action); log.debug("Mapped roles: {}", mappedRoles.toString()); } - for (final String action : actions) { - if (!securityRoles.impliesResourcePermission(action)) { + List resourcesRequested = action.getRequestedResources(); + if (resourcesRequested == null || resourcesRequested.isEmpty()) { + presponse.allowed = true; + return presponse; + } + presponse.allowed = true; + for (String resource : resourcesRequested) { + if (!securityRoles.impliesResourcePermission(resource)) { presponse.missingPrivileges.add(action); presponse.allowed = false; log.info( @@ -84,17 +98,8 @@ public PrivilegesEvaluatorResponse evaluate(final User user, final Set a securityRoles.getRoleNames(), presponse.missingPrivileges ); - } else { - if (isDebugEnabled) { - log.debug("Allowed because we have permissions for {}", actions); - } - presponse.allowed = true; - - // break the loop as we found the matching permission - break; } } - return presponse; } diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index 0e6143187f..8c833adc1d 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -506,8 +506,8 @@ public boolean isPermittedOnSystemIndex(String indexName) { } @Override - public boolean impliesResourcePermission(String action0) { - return roles.stream().filter(r -> r.impliesClusterPermission(action0)).count() > 0; + public boolean impliesResourcePermission(String resource) { + return roles.stream().filter(r -> r.impliesClusterPermission(resource)).count() > 0; } } @@ -515,11 +515,13 @@ public static class SecurityRole { private final String name; private final Set ipatterns; private final WildcardMatcher clusterPerms; + private final WildcardMatcher resourcePerms; public static final class Builder { private final String name; private final Set clusterPerms = new HashSet<>(); private final Set ipatterns = new HashSet<>(); + private final Set resourcePerms = new HashSet<>(); public Builder(String name) { this.name = Objects.requireNonNull(name); @@ -538,20 +540,22 @@ public Builder addClusterPerms(Collection clusterPerms) { } public SecurityRole build() { - return new SecurityRole(name, ipatterns, WildcardMatcher.from(clusterPerms)); + return new SecurityRole(name, ipatterns, WildcardMatcher.from(clusterPerms), WildcardMatcher.from(resourcePerms)); } } - private SecurityRole(String name, Set ipatterns, WildcardMatcher clusterPerms) { + private SecurityRole(String name, Set ipatterns, WildcardMatcher clusterPerms, WildcardMatcher resourcePerms) { this.name = Objects.requireNonNull(name); this.ipatterns = ipatterns; this.clusterPerms = clusterPerms; + this.resourcePerms = resourcePerms; } private boolean impliesClusterPermission(String action) { return clusterPerms.test(action); } + // get indices which are permitted for the given types and actions // dnfof + opensearchDashboards special only private Set getAllResolvedPermittedIndices( diff --git a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java index 0e1070c6bd..16ae95e9a8 100644 --- a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java +++ b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java @@ -29,6 +29,7 @@ import java.util.Set; +import org.opensearch.action.ActionRequest; import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; import org.opensearch.core.xcontent.NamedXContentRegistry; @@ -99,5 +100,5 @@ Set getAllPermittedIndicesForDashboards( boolean isPermittedOnSystemIndex(String indexName); - boolean impliesResourcePermission(String action0); + boolean impliesResourcePermission(String resource); } From 4cb7bc2584c606dd8027c4c961cc3c5a6d12f1af Mon Sep 17 00:00:00 2001 From: Stephen Crawford Date: Tue, 13 Aug 2024 11:19:53 -0400 Subject: [PATCH 5/7] Expand RoleV7 to include Resources Signed-off-by: Stephen Crawford --- .../privileges/ResourceAccessEvaluator.java | 36 ++++--------------- .../security/securityconf/ConfigModelV7.java | 17 +++++++++ .../security/securityconf/impl/v7/RoleV7.java | 1 + 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/ResourceAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/ResourceAccessEvaluator.java index 8f6e8ad59b..ef5f2da654 100644 --- a/src/main/java/org/opensearch/security/privileges/ResourceAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/ResourceAccessEvaluator.java @@ -11,48 +11,31 @@ package org.opensearch.security.privileges; -import java.util.List; -import java.util.Set; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - +import org.greenrobot.eventbus.Subscribe; import org.opensearch.OpenSearchSecurityException; import org.opensearch.action.ActionRequest; -import org.opensearch.cluster.metadata.IndexNameExpressionResolver; import org.opensearch.cluster.service.ClusterService; -import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.core.common.transport.TransportAddress; -import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.securityconf.ConfigModel; import org.opensearch.security.securityconf.SecurityRoles; -import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.user.User; -import org.opensearch.tasks.Task; -import org.opensearch.threadpool.ThreadPool; -import org.greenrobot.eventbus.Subscribe; +import java.util.List; +import java.util.Set; public class ResourceAccessEvaluator { protected final Logger log = LogManager.getLogger(this.getClass()); - private final ClusterService clusterService; - private ThreadContext threadContext; private ConfigModel configModel; - public ResourceAccessEvaluator(final ClusterService clusterService, final ThreadPool threadPool) { - this.clusterService = clusterService; - this.threadContext = threadPool.getThreadContext(); - } + public ResourceAccessEvaluator() {} @Subscribe public void onConfigModelChanged(final ConfigModel configModel) { this.configModel = configModel; } - SecurityRoles getSecurityRoles(final Set roles) { - return configModel.getSecurityRoles().filter(roles); - } - boolean isInitialized() { return configModel != null && configModel.getSecurityRoles() != null; } @@ -68,20 +51,15 @@ public PrivilegesEvaluatorResponse evaluate(final ActionRequest request, final PrivilegesEvaluatorResponse presponse = new PrivilegesEvaluatorResponse(); - final TransportAddress caller = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_REMOTE_ADDRESS); - - final Set mappedRoles = mapRoles(user, caller); - - presponse.resolvedSecurityRoles.addAll(mappedRoles); - final boolean isDebugEnabled = log.isDebugEnabled(); if (isDebugEnabled) { log.debug("Evaluate permissions for {} on {}", user, clusterService.localNode().getName()); log.debug("Action: {}", action); - log.debug("Mapped roles: {}", mappedRoles.toString()); + log.debug("Resource: {}", request.getRequestedResources()); + log.debug("Security roles: {}", securityRoles.toString()); } - List resourcesRequested = action.getRequestedResources(); + List resourcesRequested = request.getRequestedResources(); if (resourcesRequested == null || resourcesRequested.isEmpty()) { presponse.allowed = true; return presponse; diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index 8c833adc1d..ce3c175c60 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -63,6 +63,7 @@ import org.opensearch.security.securityconf.impl.v7.RoleMappingsV7; import org.opensearch.security.securityconf.impl.v7.RoleV7; import org.opensearch.security.securityconf.impl.v7.RoleV7.Index; +import org.opensearch.security.securityconf.impl.v7.RoleV7.Resource; import org.opensearch.security.securityconf.impl.v7.TenantV7; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.support.WildcardMatcher; @@ -179,6 +180,10 @@ public SecurityRole call() throws Exception { } + for (final Resource permittedResources : securityRole.getValue().getResource_permissions()) { + _securityRole.addResourcePerms(permittedResources.getResource_patterns()); + } + return _securityRole.build(); } }); @@ -539,6 +544,13 @@ public Builder addClusterPerms(Collection clusterPerms) { return this; } + public Builder addResourcePerms(Collection resourcePerms) { + if (resourcePerms != null) { + this.resourcePerms.addAll(resourcePerms); + } + return this; + } + public SecurityRole build() { return new SecurityRole(name, ipatterns, WildcardMatcher.from(clusterPerms), WildcardMatcher.from(resourcePerms)); } @@ -555,6 +567,11 @@ private boolean impliesClusterPermission(String action) { return clusterPerms.test(action); } + private boolean impliesResourcePermission(String action) { + return resourcePerms.test(action); + } + + // get indices which are permitted for the given types and actions // dnfof + opensearchDashboards special only diff --git a/src/main/java/org/opensearch/security/securityconf/impl/v7/RoleV7.java b/src/main/java/org/opensearch/security/securityconf/impl/v7/RoleV7.java index 73396f500b..31219d74b2 100644 --- a/src/main/java/org/opensearch/security/securityconf/impl/v7/RoleV7.java +++ b/src/main/java/org/opensearch/security/securityconf/impl/v7/RoleV7.java @@ -262,6 +262,7 @@ public List getResource_patterns() { } public void setResource_patterns(List resource_patterns) { + lastModifiedAt = new Date(); this.resource_patterns = resource_patterns; } From b317a1aa8e7f95bb536991c5991ce62bbac66f27 Mon Sep 17 00:00:00 2001 From: Stephen Crawford Date: Tue, 13 Aug 2024 11:24:48 -0400 Subject: [PATCH 6/7] Expand RoleV7 to include Resources Signed-off-by: Stephen Crawford --- .../opensearch/security/privileges/PrivilegesEvaluator.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java index 199442ee03..7568139a2e 100644 --- a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java @@ -138,6 +138,7 @@ public class PrivilegesEvaluator { private final SnapshotRestoreEvaluator snapshotRestoreEvaluator; private final SystemIndexAccessEvaluator systemIndexAccessEvaluator; private final ProtectedIndexAccessEvaluator protectedIndexAccessEvaluator; + private final ResourceAccessEvaluator resourceAccessEvaluator; private final TermsAggregationEvaluator termsAggregationEvaluator; private final PitPrivilegesEvaluator pitPrivilegesEvaluator; private DynamicConfigModel dcm; @@ -174,6 +175,7 @@ public PrivilegesEvaluator( snapshotRestoreEvaluator = new SnapshotRestoreEvaluator(settings, auditLog); systemIndexAccessEvaluator = new SystemIndexAccessEvaluator(settings, auditLog, irr); protectedIndexAccessEvaluator = new ProtectedIndexAccessEvaluator(settings, auditLog); + resourceAccessEvaluator = new ResourceAccessEvaluator(); termsAggregationEvaluator = new TermsAggregationEvaluator(); pitPrivilegesEvaluator = new PitPrivilegesEvaluator(); this.namedXContentRegistry = namedXContentRegistry; @@ -347,6 +349,10 @@ public PrivilegesEvaluatorResponse evaluate(PrivilegesEvaluationContext context) return presponse; } + if (resourceAccessEvaluator.evaluate(request, action0, securityRoles, user, clusterService).isComplete()) { + return presponse; + } + // check access for point in time requests if (pitPrivilegesEvaluator.evaluate(request, clusterService, user, securityRoles, action0, resolver, presponse, irr).isComplete()) { return presponse; From 137ad1217cfc6d0db871c3497284cbe36081673a Mon Sep 17 00:00:00 2001 From: Stephen Crawford Date: Tue, 13 Aug 2024 13:07:33 -0400 Subject: [PATCH 7/7] Expand RoleV7 to include Resources Signed-off-by: Stephen Crawford --- .../security/privileges/ResourceAccessEvaluator.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/ResourceAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/ResourceAccessEvaluator.java index ef5f2da654..bb62855137 100644 --- a/src/main/java/org/opensearch/security/privileges/ResourceAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/ResourceAccessEvaluator.java @@ -55,11 +55,11 @@ public PrivilegesEvaluatorResponse evaluate(final ActionRequest request, if (isDebugEnabled) { log.debug("Evaluate permissions for {} on {}", user, clusterService.localNode().getName()); log.debug("Action: {}", action); - log.debug("Resource: {}", request.getRequestedResources()); + log.debug("Resource: {}", request.getResources()); log.debug("Security roles: {}", securityRoles.toString()); } - List resourcesRequested = request.getRequestedResources(); + List resourcesRequested = request.getResources(); if (resourcesRequested == null || resourcesRequested.isEmpty()) { presponse.allowed = true; return presponse;