From 9277ffec9b36743c8667fd0f207cf091647640a5 Mon Sep 17 00:00:00 2001 From: Darshit Chanpura Date: Wed, 27 Sep 2023 17:19:30 -0400 Subject: [PATCH] Cleans up rest of the changes Signed-off-by: Darshit Chanpura --- .../privileges/PrivilegesEvaluator.java | 61 +- .../SecurityIndexAccessEvaluator.java | 313 ++++++--- .../security/securityconf/ConfigModelV6.java | 3 + .../security/securityconf/ConfigModelV7.java | 25 +- .../securityconf/DynamicConfigModelV7.java | 2 - .../security/securityconf/SecurityRoles.java | 2 +- .../security/support/ConfigConstants.java | 6 - .../security/user/AuthCredentials.java | 13 - .../SecurityIndexAccessEvaluatorTest.java | 606 +++++++++++++++++- 9 files changed, 847 insertions(+), 184 deletions(-) diff --git a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java index 7c3c6999fc..8120a5259f 100644 --- a/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/PrivilegesEvaluator.java @@ -36,8 +36,8 @@ import java.util.Optional; import java.util.Set; import java.util.StringJoiner; -import java.util.regex.Pattern; +import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -106,12 +106,19 @@ public class PrivilegesEvaluator { - private static final WildcardMatcher ACTION_MATCHER = WildcardMatcher.from("indices:data/read/*search*"); - - private static final Pattern DNFOF_PATTERNS = Pattern.compile( - "indices:(data/read/.*|(admin/(mappings/fields/get.*|shards/search_shards|resolve/index)))" + static final WildcardMatcher DNFOF_MATCHER = WildcardMatcher.from( + ImmutableList.of( + "indices:data/read/*", + "indices:admin/mappings/fields/get*", + "indices:admin/shards/search_shards", + "indices:admin/resolve/index", + "indices:monitor/settings/get", + "indices:monitor/stats" + ) ); + private static final WildcardMatcher ACTION_MATCHER = WildcardMatcher.from("indices:data/read/*search*"); + private static final IndicesOptions ALLOW_EMPTY = IndicesOptions.fromOptions(true, true, false, false); protected final Logger log = LogManager.getLogger(this.getClass()); @@ -195,25 +202,21 @@ private SecurityRoles getSecurityRoles(Set roles) { .lookupExtensionSettingsById(authenticatedUser.getName()); if (matchingExtension.isPresent()) { Settings permissions = (Settings) matchingExtension.get().getAdditionalSettings().get(PERMISSIONS_SETTING); - List indexPerms = permissions.getNestedListOfSettings("index_permissions"); + Settings indexPerms = permissions.getAsSettings("index_permissions"); List clusterPerms = permissions.getAsList("cluster_permissions"); - if (indexPerms != null || clusterPerms != null) { - RoleV7 newRole = new RoleV7(); - if (clusterPerms != null) { - newRole.setCluster_permissions(clusterPerms); - } - if (indexPerms != null) { - List allIndexPerms = new ArrayList<>(); - for (Settings indexPerm : indexPerms) { - RoleV7.Index indexPermissions = new RoleV7.Index(); - indexPermissions.setIndex_patterns(indexPerm.getAsList("index_patterns")); - indexPermissions.setAllowed_actions(indexPerm.getAsList("allowed_actions")); - allIndexPerms.add(indexPermissions); - } - newRole.setIndex_permissions(allIndexPerms); - } - securityRoles.addRole(newRole); + RoleV7 newRole = new RoleV7(); + if (clusterPerms != null) { + newRole.setCluster_permissions(clusterPerms); + } + if (!indexPerms.keySet().isEmpty()) { + List allIndexPerms = new ArrayList<>(); + RoleV7.Index indexPermissions = new RoleV7.Index(); + indexPermissions.setIndex_patterns(indexPerms.getAsList("index_patterns")); + indexPermissions.setAllowed_actions(indexPerms.getAsList("allowed_actions")); + allIndexPerms.add(indexPermissions); + newRole.setIndex_permissions(allIndexPerms); } + securityRoles.addRole(newRole); } return securityRoles; @@ -339,7 +342,17 @@ public PrivilegesEvaluatorResponse evaluate( } // Security index access - if (securityIndexAccessEvaluator.evaluate(request, task, action0, requestedResolved, presponse).isComplete()) { + if (securityIndexAccessEvaluator.evaluate( + request, + task, + action0, + requestedResolved, + presponse, + securityRoles, + user, + resolver, + clusterService + ).isComplete()) { return presponse; } @@ -505,7 +518,7 @@ public PrivilegesEvaluatorResponse evaluate( } } - if (dnfofEnabled && DNFOF_PATTERNS.matcher(action0).matches()) { + if (dnfofEnabled && DNFOF_MATCHER.test(action0)) { if (requestedResolved.getAllIndices().isEmpty()) { presponse.missingPrivileges.clear(); diff --git a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java index 209e6e3773..d275125a67 100644 --- a/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java +++ b/src/main/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluator.java @@ -26,19 +26,13 @@ package org.opensearch.security.privileges; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; - import org.opensearch.action.ActionRequest; import org.opensearch.action.RealtimeRequest; import org.opensearch.action.search.SearchRequest; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.ThreadContext; import org.opensearch.extensions.ExtensionsSettings; @@ -46,27 +40,44 @@ import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; +import org.opensearch.security.securityconf.SecurityRoles; import org.opensearch.security.support.ConfigConstants; import org.opensearch.security.support.WildcardMatcher; import org.opensearch.security.user.User; import org.opensearch.tasks.Task; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + import static org.opensearch.security.OpenSearchSecurityPlugin.RESERVED_INDICES_SETTING; +/** + * This class performs authorization on requests targeting system indices + * NOTE: + * - The term `protected system indices` used here translates to system indices + * which have an added layer of security and cannot be accessed by anyone except Super Admin + */ public class SecurityIndexAccessEvaluator { Logger log = LogManager.getLogger(this.getClass()); private final String securityIndex; private final AuditLog auditLog; - private final WildcardMatcher securityDeniedActionMatcher; private final IndexResolverReplacer irr; private final boolean filterSecurityIndex; - // for system-indices configuration private final WildcardMatcher systemIndexMatcher; + private final WildcardMatcher superAdminAccessOnlyIndexMatcher; + private final WildcardMatcher deniedActionsMatcher; + + private final boolean isSystemIndexEnabled; + private final boolean isSystemIndexPermissionEnabled; + private final ThreadContext threadContext; - private final boolean systemIndexEnabled; public SecurityIndexAccessEvaluator( final Settings settings, @@ -85,17 +96,33 @@ public SecurityIndexAccessEvaluator( this.systemIndexMatcher = WildcardMatcher.from( settings.getAsList(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, ConfigConstants.SECURITY_SYSTEM_INDICES_DEFAULT) ); - this.systemIndexEnabled = settings.getAsBoolean( + this.superAdminAccessOnlyIndexMatcher = WildcardMatcher.from(this.securityIndex); + this.isSystemIndexEnabled = settings.getAsBoolean( ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_DEFAULT ); - final boolean restoreSecurityIndexEnabled = settings.getAsBoolean( ConfigConstants.SECURITY_UNSUPPORTED_RESTORE_SECURITYINDEX_ENABLED, false ); - final List securityIndexDeniedActionPatternsList = new ArrayList(); + final List deniedActionPatternsList = deniedActionPatterns(); + + final List deniedActionPatternsListNoSnapshot = new ArrayList<>(deniedActionPatternsList); + deniedActionPatternsListNoSnapshot.add("indices:admin/close*"); + deniedActionPatternsListNoSnapshot.add("cluster:admin/snapshot/restore*"); + + deniedActionsMatcher = WildcardMatcher.from( + restoreSecurityIndexEnabled ? deniedActionPatternsList : deniedActionPatternsListNoSnapshot + ); + isSystemIndexPermissionEnabled = settings.getAsBoolean( + ConfigConstants.SECURITY_SYSTEM_INDICES_PERMISSIONS_ENABLED_KEY, + ConfigConstants.SECURITY_SYSTEM_INDICES_PERMISSIONS_DEFAULT + ); + } + + private static List deniedActionPatterns() { + final List securityIndexDeniedActionPatternsList = new ArrayList<>(); securityIndexDeniedActionPatternsList.add("indices:data/write*"); securityIndexDeniedActionPatternsList.add("indices:admin/delete*"); securityIndexDeniedActionPatternsList.add("indices:admin/mapping/delete*"); @@ -103,15 +130,7 @@ public SecurityIndexAccessEvaluator( securityIndexDeniedActionPatternsList.add("indices:admin/freeze*"); securityIndexDeniedActionPatternsList.add("indices:admin/settings/update*"); securityIndexDeniedActionPatternsList.add("indices:admin/aliases"); - - final List securityIndexDeniedActionPatternsListNoSnapshot = new ArrayList(); - securityIndexDeniedActionPatternsListNoSnapshot.addAll(securityIndexDeniedActionPatternsList); - securityIndexDeniedActionPatternsListNoSnapshot.add("indices:admin/close*"); - securityIndexDeniedActionPatternsListNoSnapshot.add("cluster:admin/snapshot/restore*"); - - securityDeniedActionMatcher = WildcardMatcher.from( - restoreSecurityIndexEnabled ? securityIndexDeniedActionPatternsList : securityIndexDeniedActionPatternsListNoSnapshot - ); + return securityIndexDeniedActionPatternsList; } public PrivilegesEvaluatorResponse evaluate( @@ -119,56 +138,219 @@ public PrivilegesEvaluatorResponse evaluate( final Task task, final String action, final Resolved requestedResolved, - final PrivilegesEvaluatorResponse presponse + final PrivilegesEvaluatorResponse presponse, + final SecurityRoles securityRoles, + final User user, + final IndexNameExpressionResolver resolver, + final ClusterService clusterService + ) { + evaluateSystemIndicesAccess(action, requestedResolved, request, task, presponse, securityRoles, user, resolver, clusterService); + + if (requestedResolved.isLocalAll() + || requestedResolved.getAllIndices().contains(securityIndex) + || requestContainsAnySystemIndices(requestedResolved)) { + + if (request instanceof SearchRequest) { + ((SearchRequest) request).requestCache(Boolean.FALSE); + if (log.isDebugEnabled()) { + log.debug("Disable search request cache for this request"); + } + } + + if (request instanceof RealtimeRequest) { + ((RealtimeRequest) request).realtime(Boolean.FALSE); + if (log.isDebugEnabled()) { + log.debug("Disable realtime for this request"); + } + } + } + return presponse; + } + + /** + * Checks if request is for any system index + * @param requestedResolved request which contains indices to be matched against system indices + * @return true if a match is found, false otherwise + */ + private boolean requestContainsAnySystemIndices(final Resolved requestedResolved) { + return !getAllSystemIndices(requestedResolved).isEmpty(); + } + + /** + * Gets all indices requested in the original request. + * It will always return security index if it is present in the request, as security index is protected regardless + * of feature being enabled or disabled + * @param requestedResolved request which contains indices to be matched against system indices + * @return the list of protected system indices present in the request + */ + private List getAllSystemIndices(final Resolved requestedResolved) { + final List systemIndices = requestedResolved.getAllIndices() + .stream() + .filter(securityIndex::equals) + .collect(Collectors.toList()); + if (isSystemIndexEnabled) { + systemIndices.addAll(systemIndexMatcher.getMatchAny(requestedResolved.getAllIndices(), Collectors.toList())); + } + return systemIndices; + } + + /** + * Checks if request contains any system index that is non-permission-able + * NOTE: Security index is currently non-permission-able + * @param requestedResolved request which contains indices to be matched against non-permission-able system indices + * @return true if the request contains any non-permission-able index,false otherwise + */ + private boolean requestContainsAnyProtectedSystemIndices(final Resolved requestedResolved) { + return !getAllProtectedSystemIndices(requestedResolved).isEmpty(); + } + + /** + * Filters the request to get all system indices that are protected and are non-permission-able + * @param requestedResolved request which contains indices to be matched against non-permission-able system indices + * @return the list of protected system indices present in the request + */ + private List getAllProtectedSystemIndices(final Resolved requestedResolved) { + return new ArrayList<>(superAdminAccessOnlyIndexMatcher.getMatchAny(requestedResolved.getAllIndices(), Collectors.toList())); + } + + /** + * Checks whether the request contains any of the indices reserved by extension + * @param requestedResolved request to be checked for + * @param reservedIndices the list of indices reserved by extension + * @return true if it contains reserved index or if system indices are disabled, + * false if request doesn't contain any reserved index + */ + private boolean matchAllReservedIndices(final Resolved requestedResolved, final Set reservedIndices) { + final List requestedIndexes = requestedResolved.getAllIndices() + .stream() + .filter(securityIndex::equals) + .collect(Collectors.toList()); + if (isSystemIndexEnabled) { + return reservedIndices.containsAll(requestedIndexes); + } + return true; + } + + /** + * Is the current action allowed to be performed on security index + * @param action request action on security index + * @return true if action is allowed, false otherwise + */ + private boolean isActionAllowed(String action) { + return deniedActionsMatcher.test(action); + } + + /** + * Perform access check on requested indices and actions for those indices + * @param action action to be performed on request indices + * @param requestedResolved this object contains all indices this request is resolved to + * @param request the action request to be used for audit logging + * @param task task in which this access check will be performed + * @param presponse the pre-response object that will eventually become a response and returned to the requester + * @param securityRoles user's roles which will be used for access evaluation + * @param user this user's permissions will be looked up + * @param resolver the index expression resolver + * @param clusterService required to fetch cluster state metadata + */ + private void evaluateSystemIndicesAccess( + final String action, + final Resolved requestedResolved, + final ActionRequest request, + final Task task, + final PrivilegesEvaluatorResponse presponse, + SecurityRoles securityRoles, + final User user, + final IndexNameExpressionResolver resolver, + final ClusterService clusterService ) { - System.out.println("SecurityIndexAccessEvaluator"); - final boolean isDebugEnabled = log.isDebugEnabled(); - if (securityDeniedActionMatcher.test(action)) { - System.out.println("requestedResolved: " + requestedResolved); + // Perform access check is system index permissions are enabled + boolean containsSystemIndex = requestContainsAnySystemIndices(requestedResolved); + + if (isSystemIndexPermissionEnabled) { + boolean containsProtectedIndex = requestContainsAnyProtectedSystemIndices(requestedResolved); + if (containsProtectedIndex) { + auditLog.logSecurityIndexAttempt(request, action, task); + if (log.isInfoEnabled()) { + log.info( + "{} not permitted for a regular user {} on protected system indices {}", + action, + securityRoles, + String.join(", ", getAllProtectedSystemIndices(requestedResolved)) + ); + } + presponse.allowed = false; + presponse.markComplete(); + return; + } else if (containsSystemIndex + && !securityRoles.hasExplicitIndexPermission( + requestedResolved, + user, + new String[] { ConfigConstants.SYSTEM_INDEX_PERMISSION }, + resolver, + clusterService + )) { + auditLog.logSecurityIndexAttempt(request, action, task); + if (log.isInfoEnabled()) { + log.info( + "No {} permission for user roles {} to System Indices {}", + action, + securityRoles, + String.join(", ", getAllSystemIndices(requestedResolved)) + ); + } + presponse.allowed = false; + presponse.markComplete(); + return; + } + } + + if (isActionAllowed(action)) { if (requestedResolved.isLocalAll()) { if (filterSecurityIndex) { irr.replace(request, false, "*", "-" + securityIndex); - if (isDebugEnabled) { + if (log.isDebugEnabled()) { log.debug( - "Filtered '{}'from {}, resulting list with *,-{} is {}", + "Filtered '{}' from {}, resulting list with *,-{} is {}", securityIndex, requestedResolved, securityIndex, irr.resolveRequest(request) ); } - return presponse; } else { auditLog.logSecurityIndexAttempt(request, action, task); log.warn("{} for '_all' indices is not allowed for a regular user", action); presponse.allowed = false; - return presponse.markComplete(); + presponse.markComplete(); } - } else if (matchAnySystemIndices(requestedResolved)) { - System.out.println("matchAnySystemIndices"); + } + // if system index is enabled and system index permissions are enabled we don't need to perform any further + // checks as it has already been performed via hasExplicitIndexPermission + else if (containsSystemIndex && !isSystemIndexPermissionEnabled) { if (filterSecurityIndex) { Set allWithoutSecurity = new HashSet<>(requestedResolved.getAllIndices()); allWithoutSecurity.remove(securityIndex); if (allWithoutSecurity.isEmpty()) { - if (isDebugEnabled) { + if (log.isDebugEnabled()) { log.debug("Filtered '{}' but resulting list is empty", securityIndex); } presponse.allowed = false; - return presponse.markComplete(); + presponse.markComplete(); + return; } irr.replace(request, false, allWithoutSecurity.toArray(new String[0])); - if (isDebugEnabled) { + if (log.isDebugEnabled()) { log.debug("Filtered '{}', resulting list is {}", securityIndex, allWithoutSecurity); } - return presponse; } else { User authenticatedUser = threadContext.getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER); Optional matchingExtension = OpenSearchSecurityPlugin.GuiceHolder.getExtensionsManager() .lookupExtensionSettingsById(authenticatedUser.getName()); auditLog.logSecurityIndexAttempt(request, action, task); - final String foundSystemIndexes = getProtectedIndexes(requestedResolved).stream().collect(Collectors.joining(", ")); + final String foundSystemIndexes = String.join(", ", getAllSystemIndices(requestedResolved)); if (matchingExtension.isPresent()) { - List reservedIndices = (List) matchingExtension.get() + @SuppressWarnings("unchecked") + Set reservedIndices = (Set) matchingExtension.get() .getAdditionalSettings() .get(RESERVED_INDICES_SETTING); if (matchAllReservedIndices(requestedResolved, reservedIndices)) { @@ -178,60 +360,15 @@ public PrivilegesEvaluatorResponse evaluate( foundSystemIndexes ); presponse.allowed = true; - return presponse.markComplete(); + presponse.markComplete(); + return; } } log.warn("{} for '{}' index is not allowed for a regular user", action, foundSystemIndexes); presponse.allowed = false; - return presponse.markComplete(); - } - } - } - - if (requestedResolved.isLocalAll() - || requestedResolved.getAllIndices().contains(securityIndex) - || matchAnySystemIndices(requestedResolved)) { - - if (request instanceof SearchRequest) { - ((SearchRequest) request).requestCache(Boolean.FALSE); - if (isDebugEnabled) { - log.debug("Disable search request cache for this request"); + presponse.markComplete(); } } - - if (request instanceof RealtimeRequest) { - ((RealtimeRequest) request).realtime(Boolean.FALSE); - if (isDebugEnabled) { - log.debug("Disable realtime for this request"); - } - } - } - return presponse; - } - - private boolean matchAnySystemIndices(final Resolved requestedResolved) { - return !getProtectedIndexes(requestedResolved).isEmpty(); - } - - private List getProtectedIndexes(final Resolved requestedResolved) { - final List protectedIndexes = requestedResolved.getAllIndices() - .stream() - .filter(securityIndex::equals) - .collect(Collectors.toList()); - if (systemIndexEnabled) { - protectedIndexes.addAll(systemIndexMatcher.getMatchAny(requestedResolved.getAllIndices(), Collectors.toList())); } - return protectedIndexes; - } - - private boolean matchAllReservedIndices(final Resolved requestedResolved, final List reservedIndices) { - final List requestedIndexes = requestedResolved.getAllIndices() - .stream() - .filter(securityIndex::equals) - .collect(Collectors.toList()); - if (systemIndexEnabled) { - return reservedIndices.containsAll(requestedIndexes); - } - return true; } } diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java index 84109c845e..fa029a026f 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV6.java @@ -348,6 +348,9 @@ public Set getRoleNames() { return getRoles().stream().map(r -> r.getName()).collect(Collectors.toSet()); } + @Override + public void addRole(Role role) {} + public SecurityRoles filter(Set keep) { final SecurityRoles retVal = new SecurityRoles(roles.size()); for (SecurityRole sr : roles) { diff --git a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java index 99ff983a7b..bfcfcabf7c 100644 --- a/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/ConfigModelV7.java @@ -348,17 +348,6 @@ public void addRole(Role role) { final Set permittedClusterActions = agr.resolvedActions(roleToAdd.getCluster_permissions()); _securityRole.addClusterPerms(permittedClusterActions); - /*for(RoleV7.Tenant tenant: securityRole.getValue().getTenant_permissions()) { - //if(tenant.equals(user.getName())) { - // continue; - //} - if(isTenantsRw(tenant)) { - _securityRole.addTenant(new Tenant(tenant.getKey(), true)); - } else { - _securityRole.addTenant(new Tenant(tenant.getKey(), false)); - } - }*/ - for (final Index permittedAliasesIndex : roleToAdd.getIndex_permissions()) { final String dls = permittedAliasesIndex.getDls(); @@ -371,18 +360,8 @@ public void addRole(Role role) { _indexPattern.addFlsFields(fls); _indexPattern.addMaskedFields(maskedFields); _indexPattern.addPerm(agr.resolvedActions(permittedAliasesIndex.getAllowed_actions())); - - /*for(Entry> type: permittedAliasesIndex.getValue().getTypes(-).entrySet()) { - TypePerm typePerm = new TypePerm(type.getKey()); - final List perms = type.getValue(); - typePerm.addPerms(agr.resolvedActions(perms)); - _indexPattern.addTypePerms(typePerm); - }*/ - _securityRole.addIndexPattern(_indexPattern); - } - } SecurityRole newRole = _securityRole.build(); @@ -559,7 +538,7 @@ public boolean hasExplicitClusterPermissionPermission(String action) { return roles.stream().map(r -> matchExplicitly(r.clusterPerms)).filter(m -> m.test(action)).count() > 0; } - private static WildcardMatcher matchExplicitly(final WildcardMatcher matcher) { + WildcardMatcher matchExplicitly(final WildcardMatcher matcher) { return matcher == WildcardMatcher.ANY ? WildcardMatcher.NONE : matcher; } @@ -579,7 +558,7 @@ public boolean hasExplicitIndexPermission( } final Set explicitlyAllowedIndices = roles.stream() - .map(role -> role.getAllResolvedPermittedIndices(resolved, user, actions, resolver, cs, SecurityRoles::matchExplicitly)) + .map(role -> role.getAllResolvedPermittedIndices(resolved, user, actions, resolver, cs, this::matchExplicitly)) .flatMap(Collection::stream) .collect(Collectors.toSet()); diff --git a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java index 00c4d994b5..1ccd78b8cb 100644 --- a/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java +++ b/src/main/java/org/opensearch/security/securityconf/DynamicConfigModelV7.java @@ -62,9 +62,7 @@ import org.opensearch.security.securityconf.impl.v7.ConfigV7.Authz; import org.opensearch.security.securityconf.impl.v7.ConfigV7.AuthzDomain; import org.opensearch.security.support.ReflectionHelper; -import org.opensearch.security.auth.internal.NoOpAuthenticationBackend; import org.opensearch.security.configuration.ClusterInfoHolder; -import org.opensearch.security.http.OnBehalfOfAuthenticator; import static org.opensearch.security.identity.SecurityTokenManager.DEMO_SETTINGS; diff --git a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java index 48a9b1ed1c..ed99d353b1 100644 --- a/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java +++ b/src/main/java/org/opensearch/security/securityconf/SecurityRoles.java @@ -55,7 +55,7 @@ boolean hasExplicitIndexPermission( Set getRoleNames(); - default void addRole(Role role) {}; + void addRole(Role role); Set reduce( Resolved requestedResolved, diff --git a/src/main/java/org/opensearch/security/support/ConfigConstants.java b/src/main/java/org/opensearch/security/support/ConfigConstants.java index 1475ceb17c..e5cc566133 100644 --- a/src/main/java/org/opensearch/security/support/ConfigConstants.java +++ b/src/main/java/org/opensearch/security/support/ConfigConstants.java @@ -321,12 +321,6 @@ public enum RolesMappingResolution { public static final String TENANCY_GLOBAL_TENANT_NAME = "global"; public static final String TENANCY_GLOBAL_TENANT_DEFAULT_NAME = ""; - // On-behalf-of endpoints settings - // CS-SUPPRESS-SINGLE: RegexpSingleline get Extensions Settings - public static final String EXTENSIONS_BWC_PLUGIN_MODE = "bwcPluginMode"; - public static final boolean EXTENSIONS_BWC_PLUGIN_MODE_DEFAULT = false; - // CS-ENFORCE-SINGLE - public static Set getSettingAsSet( final Settings settings, final String key, diff --git a/src/main/java/org/opensearch/security/user/AuthCredentials.java b/src/main/java/org/opensearch/security/user/AuthCredentials.java index b28722292a..06cbe92437 100644 --- a/src/main/java/org/opensearch/security/user/AuthCredentials.java +++ b/src/main/java/org/opensearch/security/user/AuthCredentials.java @@ -33,7 +33,6 @@ import java.util.Collections; import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Set; @@ -109,18 +108,6 @@ public AuthCredentials(final String username, Collection securityRoles, this.securityRoles.addAll(securityRoles); } - /** - * Create new credentials with a username, a initial optional set of roles and empty password/native credentials - * @param username The username, must not be null or empty - * @param securityRoles The internal roles the user has been mapped to - * @param backendRoles set of roles this user is a member of - * @throws IllegalArgumentException if username is null or empty - */ - public AuthCredentials(final String username, List securityRoles, String... backendRoles) { - this(username, null, null, backendRoles); - this.securityRoles.addAll(securityRoles); - } - private AuthCredentials(final String username, byte[] password, Object nativeCredentials, String... backendRoles) { super(); diff --git a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java index 1d96730fde..af5b92dff0 100644 --- a/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java +++ b/src/test/java/org/opensearch/security/privileges/SecurityIndexAccessEvaluatorTest.java @@ -14,7 +14,6 @@ import com.google.common.collect.ImmutableSet; import org.apache.logging.log4j.Logger; import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -24,21 +23,40 @@ import org.opensearch.action.get.MultiGetRequest; import org.opensearch.action.search.SearchRequest; import org.opensearch.action.support.IndicesOptions; +import org.opensearch.cluster.metadata.IndexNameExpressionResolver; +import org.opensearch.cluster.service.ClusterService; import org.opensearch.common.settings.Settings; import org.opensearch.common.util.concurrent.ThreadContext; +import org.opensearch.extensions.ExtensionsManager; +import org.opensearch.security.OpenSearchSecurityPlugin; import org.opensearch.security.auditlog.AuditLog; import org.opensearch.security.resolver.IndexResolverReplacer; import org.opensearch.security.resolver.IndexResolverReplacer.Resolved; +import org.opensearch.security.securityconf.ConfigModelV7; +import org.opensearch.security.securityconf.SecurityRoles; +import org.opensearch.security.support.ConfigConstants; +import org.opensearch.security.user.User; import org.opensearch.tasks.Task; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Optional; +import java.util.Set; + import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; +import static org.opensearch.security.support.ConfigConstants.SYSTEM_INDEX_PERMISSION; @RunWith(MockitoJUnitRunner.class) public class SecurityIndexAccessEvaluatorTest { @@ -56,19 +74,81 @@ public class SecurityIndexAccessEvaluatorTest { @Mock private Logger log; @Mock - private ThreadContext threadContext; + ClusterService cs; private SecurityIndexAccessEvaluator evaluator; - private static final String UNPROTECTED_ACTION = "indices:data/read"; private static final String PROTECTED_ACTION = "indices:data/write"; - @Before - public void before() { + private static final String TEST_SYSTEM_INDEX = ".test_system_index"; + private static final String TEST_INDEX = ".test"; + private static final String SECURITY_INDEX = ConfigConstants.OPENDISTRO_SECURITY_DEFAULT_CONFIG_INDEX; + + @Mock + SecurityRoles securityRoles; + @Mock + ExtensionsManager extensionsManagerMock; + + User user; + + IndexNameExpressionResolver indexNameExpressionResolver; + + private ThreadContext createThreadContext() { + return new ThreadContext(Settings.EMPTY); + } + + protected IndexNameExpressionResolver createIndexNameExpressionResolver(ThreadContext threadContext) { + return new IndexNameExpressionResolver(threadContext); + } + + public void setup( + boolean isSystemIndexEnabled, + boolean isSystemIndexPermissionsEnabled, + String index, + boolean createIndexPatternWithSystemIndexPermission + ) { + ThreadContext threadContext = createThreadContext(); + indexNameExpressionResolver = createIndexNameExpressionResolver(threadContext); + + // create a security role + ConfigModelV7.IndexPattern ip = spy(new ConfigModelV7.IndexPattern(index)); + ConfigModelV7.SecurityRole.Builder _securityRole = new ConfigModelV7.SecurityRole.Builder("role_a"); + ip.addPerm(createIndexPatternWithSystemIndexPermission ? Set.of("*", SYSTEM_INDEX_PERMISSION) : Set.of("*")); + _securityRole.addIndexPattern(ip); + _securityRole.addClusterPerms(List.of("*")); + ConfigModelV7.SecurityRole secRole = _securityRole.build(); + + try { + // create an instance of Security Role + @SuppressWarnings("unchecked") + Constructor constructor = (Constructor< + ConfigModelV7.SecurityRoles>) ConfigModelV7.SecurityRoles.class.getDeclaredConstructors()[0]; + constructor.setAccessible(true); + securityRoles = constructor.newInstance(null, 1); + + // add security role to Security Roles + Method addSecurityRoleMethod = ConfigModelV7.SecurityRoles.class.getDeclaredMethod( + "addSecurityRole", + ConfigModelV7.SecurityRole.class + ); + addSecurityRoleMethod.setAccessible(true); + addSecurityRoleMethod.invoke(securityRoles, secRole); + + } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } + + // create a user and associate them with the role + user = new User("user_a"); + user.addSecurityRoles(List.of("role_a")); + + // when trying to resolve Index Names + evaluator = new SecurityIndexAccessEvaluator( - Settings.EMPTY.builder() - .put("plugins.security.system_indices.indices", ".test") - .put("plugins.security.system_indices.enabled", true) + Settings.builder() + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_KEY, TEST_SYSTEM_INDEX) + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_ENABLED_KEY, isSystemIndexEnabled) + .put(ConfigConstants.SECURITY_SYSTEM_INDICES_PERMISSIONS_ENABLED_KEY, isSystemIndexPermissionsEnabled) .build(), auditLog, irr, @@ -76,7 +156,23 @@ public void before() { ); evaluator.log = log; + threadContext.putTransient(ConfigConstants.OPENDISTRO_SECURITY_USER, user); + + Field field; + try { + field = OpenSearchSecurityPlugin.GuiceHolder.class.getDeclaredField("extensionsManager"); + field.setAccessible(true); + field.set(null, extensionsManagerMock); + } catch (NoSuchFieldException | IllegalAccessException e) { + throw new RuntimeException(e); + } + when(extensionsManagerMock.lookupExtensionSettingsById(user.getName())).thenReturn(Optional.empty()); + when(log.isDebugEnabled()).thenReturn(true); + when(log.isInfoEnabled()).thenReturn(true); + + doReturn(ImmutableSet.of(index)).when(ip).getResolvedIndexPattern(user, indexNameExpressionResolver, cs, true); + } @After @@ -85,66 +181,522 @@ public void after() { } @Test - public void actionIsNotProtected_noSystemIndexInvolved() { - final Resolved resolved = createResolved(".test"); + public void testUnprotectedActionOnRegularIndex_systemIndexDisabled() { + setup(false, false, TEST_INDEX, false); + final Resolved resolved = createResolved(TEST_INDEX); // Action - final PrivilegesEvaluatorResponse response = evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse); + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + verifyNoInteractions(presponse); + assertThat(response, is(presponse)); + } + + @Test + public void testUnprotectedActionOnRegularIndex_systemIndexPermissionDisabled() { + setup(true, false, TEST_INDEX, false); + final Resolved resolved = createResolved(TEST_INDEX); + + // Action + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + verifyNoInteractions(presponse); + assertThat(response, is(presponse)); + } + + @Test + public void testUnprotectedActionOnRegularIndex_systemIndexPermissionEnabled() { + setup(true, true, TEST_INDEX, false); + final Resolved resolved = createResolved(TEST_INDEX); + + // Action + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + verifyNoInteractions(presponse); + assertThat(response, is(presponse)); + } + + @Test + public void testUnprotectedActionOnSystemIndex_systemIndexDisabled() { + setup(false, false, TEST_SYSTEM_INDEX, false); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + verifyNoInteractions(presponse); + assertThat(response, is(presponse)); + } + + @Test + public void testUnprotectedActionOnSystemIndex_systemIndexPermissionDisabled() { + setup(true, false, TEST_SYSTEM_INDEX, false); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); verifyNoInteractions(presponse); assertThat(response, is(presponse)); + } + + @Test + public void testUnprotectedActionOnSystemIndex_systemIndexPermissionEnabled_WithoutSystemIndexPermission() { + setup(true, true, TEST_SYSTEM_INDEX, false); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + verify(presponse).markComplete(); + assertThat(response, is(presponse)); - verify(log).isDebugEnabled(); + verify(auditLog).logSecurityIndexAttempt(request, UNPROTECTED_ACTION, null); + verify(log).isInfoEnabled(); + verify(log).info("No {} permission for user roles {} to System Indices {}", UNPROTECTED_ACTION, securityRoles, TEST_SYSTEM_INDEX); + } + + @Test + public void testUnprotectedActionOnSystemIndex_systemIndexPermissionEnabled_WithSystemIndexPermission() { + setup(true, true, TEST_SYSTEM_INDEX, true); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + final PrivilegesEvaluatorResponse response = evaluator.evaluate( + request, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + assertThat(response, is(presponse)); + // unprotected action is not allowed on a system index + assertThat(presponse.allowed, is(false)); } @Test - public void disableCacheOrRealtimeOnSystemIndex() { + public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexDisabled() { + setup(false, false, TEST_SYSTEM_INDEX, false); + final SearchRequest searchRequest = mock(SearchRequest.class); final MultiGetRequest realtimeRequest = mock(MultiGetRequest.class); - final Resolved resolved = createResolved(".test"); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); // Action - evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse); - evaluator.evaluate(searchRequest, null, UNPROTECTED_ACTION, resolved, presponse); - evaluator.evaluate(realtimeRequest, null, UNPROTECTED_ACTION, resolved, presponse); + evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + evaluator.evaluate( + searchRequest, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + evaluator.evaluate( + realtimeRequest, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); verifyNoInteractions(presponse); + } + + @Test + public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionDisabled() { + setup(true, false, TEST_SYSTEM_INDEX, false); + + final SearchRequest searchRequest = mock(SearchRequest.class); + final MultiGetRequest realtimeRequest = mock(MultiGetRequest.class); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + evaluator.evaluate( + searchRequest, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + evaluator.evaluate( + realtimeRequest, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + verify(searchRequest).requestCache(Boolean.FALSE); verify(realtimeRequest).realtime(Boolean.FALSE); - verify(log, times(3)).isDebugEnabled(); + verify(log, times(2)).isDebugEnabled(); verify(log).debug("Disable search request cache for this request"); verify(log).debug("Disable realtime for this request"); } @Test - public void protectedActionLocalAll() { + public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionEnabled_withoutSystemIndexPermission() { + setup(true, true, TEST_SYSTEM_INDEX, false); + + final SearchRequest searchRequest = mock(SearchRequest.class); + final MultiGetRequest realtimeRequest = mock(MultiGetRequest.class); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + evaluator.evaluate( + searchRequest, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + evaluator.evaluate( + realtimeRequest, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + + verify(searchRequest).requestCache(Boolean.FALSE); + verify(realtimeRequest).realtime(Boolean.FALSE); + + verify(log, times(2)).isDebugEnabled(); + verify(log).debug("Disable search request cache for this request"); + verify(log).debug("Disable realtime for this request"); + verify(auditLog).logSecurityIndexAttempt(request, UNPROTECTED_ACTION, null); + verify(auditLog).logSecurityIndexAttempt(searchRequest, UNPROTECTED_ACTION, null); + verify(auditLog).logSecurityIndexAttempt(realtimeRequest, UNPROTECTED_ACTION, null); + verify(presponse, times(3)).markComplete(); + verify(log, times(2)).isDebugEnabled(); + verify(log, times(3)).isInfoEnabled(); + verify(log, times(3)).info( + "No {} permission for user roles {} to System Indices {}", + UNPROTECTED_ACTION, + securityRoles, + TEST_SYSTEM_INDEX + ); + verify(log).debug("Disable search request cache for this request"); + verify(log).debug("Disable realtime for this request"); + } + + @Test + public void testDisableCacheOrRealtimeOnSystemIndex_systemIndexPermissionEnabled_withSystemIndexPermission() { + setup(true, true, TEST_SYSTEM_INDEX, true); + + final SearchRequest searchRequest = mock(SearchRequest.class); + final MultiGetRequest realtimeRequest = mock(MultiGetRequest.class); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + evaluator.evaluate(request, null, UNPROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + evaluator.evaluate( + searchRequest, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + evaluator.evaluate( + realtimeRequest, + null, + UNPROTECTED_ACTION, + resolved, + presponse, + securityRoles, + user, + indexNameExpressionResolver, + cs + ); + + verify(searchRequest).requestCache(Boolean.FALSE); + verify(realtimeRequest).realtime(Boolean.FALSE); + + verify(log, times(2)).isDebugEnabled(); + verify(log).debug("Disable search request cache for this request"); + verify(log).debug("Disable realtime for this request"); + } + + @Test + public void testProtectedActionLocalAll_systemIndexDisabled() { + setup(false, false, TEST_SYSTEM_INDEX, false); final Resolved resolved = Resolved._LOCAL_ALL; // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse); + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); - - verify(log).isDebugEnabled(); verify(log).warn("{} for '_all' indices is not allowed for a regular user", "indices:data/write"); } @Test - public void protectedActionSystemIndex() { - final Resolved resolved = createResolved(".test", ".opendistro_security"); + public void testProtectedActionLocalAll_systemIndexPermissionDisabled() { + setup(true, false, TEST_SYSTEM_INDEX, false); + final Resolved resolved = Resolved._LOCAL_ALL; // Action - evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse); + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); assertThat(presponse.allowed, is(false)); verify(presponse).markComplete(); + verify(log).warn("{} for '_all' indices is not allowed for a regular user", PROTECTED_ACTION); + } + + @Test + public void testProtectedActionLocalAll_systemIndexPermissionEnabled() { + setup(true, true, TEST_SYSTEM_INDEX, false); + final Resolved resolved = Resolved._LOCAL_ALL; + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + + verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); + assertThat(presponse.allowed, is(false)); + verify(presponse).markComplete(); + verify(log).warn("{} for '_all' indices is not allowed for a regular user", PROTECTED_ACTION); + } + + @Test + public void testProtectedActionOnRegularIndex_systemIndexDisabled() { + setup(false, false, TEST_INDEX, false); + final Resolved resolved = createResolved(TEST_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + + assertThat(presponse.allowed, is(false)); + } + + @Test + public void testProtectedActionOnRegularIndex_systemIndexPermissionDisabled() { + setup(true, false, TEST_INDEX, false); + final Resolved resolved = createResolved(TEST_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + + assertThat(presponse.allowed, is(false)); + } + + @Test + public void testProtectedActionOnRegularIndex_systemIndexPermissionEnabled() { + setup(true, true, TEST_INDEX, false); + final Resolved resolved = createResolved(TEST_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + + assertThat(presponse.allowed, is(false)); + } + + @Test + public void testProtectedActionOnSystemIndex_systemIndexDisabled() { + setup(false, false, TEST_SYSTEM_INDEX, false); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + + assertThat(presponse.allowed, is(false)); + } + + @Test + public void testProtectedActionOnSystemIndex_systemIndexPermissionDisabled() { + setup(true, false, TEST_SYSTEM_INDEX, false); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + + verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); + assertThat(presponse.allowed, is(false)); + verify(presponse).markComplete(); + verify(log).warn("{} for '{}' index is not allowed for a regular user", PROTECTED_ACTION, TEST_SYSTEM_INDEX); + } + + @Test + public void testProtectedActionOnSystemIndex_systemIndexPermissionEnabled_withoutSystemIndexPermission() { + setup(true, true, TEST_SYSTEM_INDEX, false); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + + verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); + assertThat(presponse.allowed, is(false)); + verify(presponse).markComplete(); + verify(log).isInfoEnabled(); + verify(log).info("No {} permission for user roles {} to System Indices {}", PROTECTED_ACTION, securityRoles, TEST_SYSTEM_INDEX); + } + + @Test + public void testProtectedActionOnSystemIndex_systemIndexPermissionEnabled_withSystemIndexPermission() { + + setup(true, true, TEST_SYSTEM_INDEX, true); + final Resolved resolved = createResolved(TEST_SYSTEM_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + + assertThat(presponse.allowed, is(false)); + } + + @Test + public void testProtectedActionOnProtectedSystemIndex_systemIndexDisabled() { + setup(false, false, SECURITY_INDEX, false); + final Resolved resolved = createResolved(SECURITY_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + + verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); + assertThat(presponse.allowed, is(false)); + verify(presponse).markComplete(); + + verify(log).warn("{} for '{}' index is not allowed for a regular user", PROTECTED_ACTION, SECURITY_INDEX); + } + + @Test + public void testProtectedActionOnProtectedSystemIndex_systemIndexPermissionDisabled() { + setup(true, false, SECURITY_INDEX, false); + final Resolved resolved = createResolved(SECURITY_INDEX); + + // Action + evaluator.evaluate(request, task, PROTECTED_ACTION, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + + verify(auditLog).logSecurityIndexAttempt(request, PROTECTED_ACTION, task); + assertThat(presponse.allowed, is(false)); + verify(presponse).markComplete(); + + verify(log).warn("{} for '{}' index is not allowed for a regular user", PROTECTED_ACTION, SECURITY_INDEX); + } + + @Test + public void testUnprotectedActionOnProtectedSystemIndex_systemIndexPermissionEnabled_withoutSystemIndexPermission() { + testSecurityIndexAccess(UNPROTECTED_ACTION); + } + + @Test + public void testUnprotectedActionOnProtectedSystemIndex_systemIndexPermissionEnabled_withSystemIndexPermission() { + testSecurityIndexAccess(UNPROTECTED_ACTION); + } + + @Test + public void testProtectedActionOnProtectedSystemIndex_systemIndexPermissionEnabled_withoutSystemIndexPermission() { + testSecurityIndexAccess(PROTECTED_ACTION); + } + + @Test + public void testProtectedActionOnProtectedSystemIndex_systemIndexPermissionEnabled_withSystemIndexPermission() { + testSecurityIndexAccess(PROTECTED_ACTION); + } + + private void testSecurityIndexAccess(String action) { + setup(true, true, SECURITY_INDEX, true); + + final Resolved resolved = createResolved(SECURITY_INDEX); + + // Action + evaluator.evaluate(request, task, action, resolved, presponse, securityRoles, user, indexNameExpressionResolver, cs); + + verify(auditLog).logSecurityIndexAttempt(request, action, task); + assertThat(presponse.allowed, is(false)); + verify(presponse).markComplete(); - verify(log).isDebugEnabled(); - verify(log).warn("{} for '{}' index is not allowed for a regular user", "indices:data/write", ".opendistro_security, .test"); + verify(log).isInfoEnabled(); + verify(log).info("{} not permitted for a regular user {} on protected system indices {}", action, securityRoles, SECURITY_INDEX); } private Resolved createResolved(final String... indexes) {