Skip to content

Commit

Permalink
remove permission validation
Browse files Browse the repository at this point in the history
Signed-off-by: Derek Ho <[email protected]>
  • Loading branch information
derek-ho committed Feb 11, 2025
1 parent bf31791 commit c95d256
Show file tree
Hide file tree
Showing 2 changed files with 0 additions and 215 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,26 +15,21 @@
import java.nio.file.Path;
import java.time.Instant;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import org.opensearch.OpenSearchException;
import org.opensearch.client.node.NodeClient;
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.core.action.ActionListener;
import org.opensearch.core.common.transport.TransportAddress;
import org.opensearch.core.rest.RestStatus;
import org.opensearch.core.xcontent.XContentBuilder;
import org.opensearch.rest.BaseRestHandler;
Expand All @@ -49,19 +44,9 @@
import org.opensearch.security.dlic.rest.api.RestApiPrivilegesEvaluator;
import org.opensearch.security.dlic.rest.api.SecurityApiDependencies;
import org.opensearch.security.dlic.rest.support.Utils;
import org.opensearch.security.privileges.IndexPattern;
import org.opensearch.security.privileges.PrivilegesEvaluationException;
import org.opensearch.security.privileges.PrivilegesEvaluator;
import org.opensearch.security.securityconf.DynamicConfigFactory;
import org.opensearch.security.securityconf.FlattenedActionGroups;
import org.opensearch.security.securityconf.impl.CType;
import org.opensearch.security.securityconf.impl.SecurityDynamicConfiguration;
import org.opensearch.security.securityconf.impl.v7.ActionGroupsV7;
import org.opensearch.security.securityconf.impl.v7.RoleV7;
import org.opensearch.security.ssl.transport.PrincipalExtractor;
import org.opensearch.security.support.ConfigConstants;
import org.opensearch.security.support.WildcardMatcher;
import org.opensearch.security.user.User;
import org.opensearch.threadpool.ThreadPool;

import static org.opensearch.rest.RestRequest.Method.DELETE;
Expand Down Expand Up @@ -197,8 +182,6 @@ private RestChannelConsumer handlePost(RestRequest request, NodeClient client) {
List<String> clusterPermissions = extractClusterPermissions(requestBody);
List<ApiToken.IndexPermission> indexPermissions = extractIndexPermissions(requestBody);

validateUserPermissions(clusterPermissions, indexPermissions);

String token = apiTokenRepository.createApiToken(
(String) requestBody.get(NAME_FIELD),
clusterPermissions,
Expand Down Expand Up @@ -364,99 +347,6 @@ private void sendErrorResponse(RestChannel channel, RestStatus status, String er
}
}

/**
* Validates that the user has the required permissions to create an API token (must be a subset of their own permissions)
* */
@SuppressWarnings("unchecked")
void validateUserPermissions(List<String> clusterPermissions, List<ApiToken.IndexPermission> indexPermissions)
throws PrivilegesEvaluationException {
final User user = threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_USER);
final TransportAddress caller = threadPool.getThreadContext().getTransient(ConfigConstants.OPENDISTRO_SECURITY_REMOTE_ADDRESS);
final Set<String> roles = privilegesEvaluator.mapRoles(user, caller);

// Early return conditions
if (roles.isEmpty()) {
throw new OpenSearchException("User does not have any roles");
}

// Verify user has all requested cluster permissions
final SecurityDynamicConfiguration<ActionGroupsV7> actionGroupsConfiguraiton = (SecurityDynamicConfiguration<ActionGroupsV7>) load(
CType.ACTIONGROUPS
);
FlattenedActionGroups flattenedActionGroups = new FlattenedActionGroups(actionGroupsConfiguraiton);
final SecurityDynamicConfiguration<?> rolesConfiguration = load(CType.ROLES);
ImmutableSet<String> resolvedClusterPermissions = flattenedActionGroups.resolve(clusterPermissions);
Set<String> clusterPermissionsWithoutActionGroups = resolvedClusterPermissions.stream()
.filter(permission -> !actionGroupsConfiguraiton.getCEntries().containsKey(permission))
.collect(Collectors.toSet());

// Load all roles the user has access to, remove permissions that
for (String role : roles) {
RoleV7 roleV7 = (RoleV7) rolesConfiguration.getCEntry(role);
ImmutableSet<String> expandedRoleClusterPermissions = flattenedActionGroups.resolve(roleV7.getCluster_permissions());
for (String clusterPermission : expandedRoleClusterPermissions) {
WildcardMatcher matcher = WildcardMatcher.from(clusterPermission);
clusterPermissionsWithoutActionGroups.removeIf(matcher);
}
}

if (!clusterPermissionsWithoutActionGroups.isEmpty()) {
throw new OpenSearchException("User does not have all requested cluster permissions");
}

// Verify user has all requested index permissions
for (ApiToken.IndexPermission requestedPermission : indexPermissions) {
// First, flatten/resolve any action groups into the underlying actions and remove action group names (which may not exact
// match)
Set<String> resolvedActions = new HashSet<>(flattenedActionGroups.resolve(requestedPermission.getAllowedActions()));
resolvedActions.removeIf(permission -> actionGroupsConfiguraiton.getCEntries().containsKey(permission));

// For each index pattern in the requested permission
for (String requestedPattern : requestedPermission.getIndexPatterns()) {

Set<String> actionsForIndexPattern = new HashSet<>(resolvedActions);

// Check each role the user has
for (String roleName : roles) {
RoleV7 role = (RoleV7) rolesConfiguration.getCEntry(roleName);
if (role == null || role.getIndex_permissions() == null) continue;

// Check each index permission block in the role
for (RoleV7.Index indexPermission : role.getIndex_permissions()) {
List<String> rolePatterns = indexPermission.getIndex_patterns();
List<String> roleIndexPerms = indexPermission.getAllowed_actions();

IndexPattern indexPattern = IndexPattern.from(rolePatterns);

// Check if this role's pattern covers the requested pattern
if (indexPattern.matches(
requestedPattern,
indexNameExpressionResolver,
(String template) -> WildcardMatcher.NONE,
clusterService.state().metadata().getIndicesLookup()
)) {
// Get resolved actions for this role's index permissions
Set<String> roleActions = flattenedActionGroups.resolve(roleIndexPerms);
WildcardMatcher matcher = WildcardMatcher.from(roleActions);

actionsForIndexPattern.removeIf(matcher);
}
}
}

// After checking all roles, verify if all requested actions were covered
if (!actionsForIndexPattern.isEmpty()) {
throw new OpenSearchException("User does not have sufficient permissions for index pattern: " + requestedPattern);
}
}
}
}

private SecurityDynamicConfiguration<?> load(final CType<?> config) {
SecurityDynamicConfiguration<?> loaded = configurationRepository.getConfiguration(config);
return DynamicConfigFactory.addStatics(loaded);
}

protected void authorizeSecurityAccess(RestRequest request) throws IOException {
// Check if user has security API access
if (!(securityApiDependencies.restApiAdminPrivilegesEvaluator().isCurrentUserAdminFor(Endpoint.APITOKENS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,13 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.common.collect.ImmutableMap;
import com.fasterxml.jackson.core.JsonProcessingException;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.opensearch.OpenSearchException;
import org.opensearch.Version;
import org.opensearch.cluster.ClusterState;
import org.opensearch.cluster.metadata.AliasMetadata;
Expand Down Expand Up @@ -253,107 +251,4 @@ public void testExtractClusterPermissions() {
requestBody.put("cluster_permissions", Arrays.asList("perm1", "perm2"));
assertThat(apiTokenAction.extractClusterPermissions(requestBody), is(Arrays.asList("perm1", "perm2")));
}

@Test
public void testExactMatchPermissionsWithActionGroups() throws Exception {
when(privilegesEvaluator.mapRoles(null, null)).thenReturn(Set.of("read_group_logs-123"));

apiTokenAction.validateUserPermissions(List.of(), List.of(new ApiToken.IndexPermission(
List.of("logs-123"),
List.of("read_group")
)));
}

@Test
public void testCreateWildcardPermissionWhenNoAccessThrowsException() {
when(privilegesEvaluator.mapRoles(null, null)).thenReturn(Set.of("read_group_logs-123"));

assertThrows(OpenSearchException.class, () -> apiTokenAction.validateUserPermissions(List.of(), List.of(new ApiToken.IndexPermission(List.of("logs-*"), List.of("read")))));
}

@Test
public void testCreateMorePermissableWildcardPermissionWhenNoAccessThrowsException() {
when(privilegesEvaluator.mapRoles(null, null)).thenReturn(Set.of("write_group_logs-star"));

assertThrows(OpenSearchException.class, () -> apiTokenAction.validateUserPermissions(List.of(), List.of(new ApiToken.IndexPermission(List.of("lo-*"), List.of("write")))));
}

@Test
public void testMultipleRolesCoveringPermissions() throws Exception {
when(privilegesEvaluator.mapRoles(null, null)).thenReturn(Set.of("read_group_logs-123", "write_group_logs-123"));

ApiToken.IndexPermission requestedPerm = new ApiToken.IndexPermission(List.of("logs-123"), List.of("read", "write"));

apiTokenAction.validateUserPermissions(List.of(), List.of(requestedPerm));
}

@Test
public void testInsufficientPermissions() {
when(privilegesEvaluator.mapRoles(null, null)).thenReturn(Set.of("read_group_logs-star"));

ApiToken.IndexPermission requestedPerm = new ApiToken.IndexPermission(List.of("logs-2023"), List.of("read", "write"));

assertThrows(OpenSearchException.class, () -> apiTokenAction.validateUserPermissions(List.of(), List.of(requestedPerm)));
}

@Test
public void testSeparateIndexPatternThrowsException() {
when(privilegesEvaluator.mapRoles(null, null)).thenReturn(Set.of("read_group_logs-123"));

ApiToken.IndexPermission requestedPerm = new ApiToken.IndexPermission(List.of("logs-123", "metrics-2023"), List.of("read"));

assertThrows(OpenSearchException.class, () -> apiTokenAction.validateUserPermissions(List.of(), List.of(requestedPerm)));
}

@Test
public void testActionGroupResolution() throws Exception{
when(privilegesEvaluator.mapRoles(null, null)).thenReturn(Set.of("read_group_logs-123", "write_group_logs-123"));

ApiToken.IndexPermission requestedPerm = new ApiToken.IndexPermission(
List.of("logs-123"),
List.of("read", "write", "get", "create")
);

apiTokenAction.validateUserPermissions(List.of(), List.of(requestedPerm));
}

@Test
public void testEmptyIndexPermissions() throws Exception {
when(privilegesEvaluator.mapRoles(null, null)).thenReturn(Set.of("read_group_logs-123", "write_group_logs-123"));

apiTokenAction.validateUserPermissions(List.of("cluster:monitor"), List.of());
}

@Test
public void testClusterPermissionsEvaluation() throws Exception {
when(privilegesEvaluator.mapRoles(null, null)).thenReturn(Set.of("cluster_monitor"));

apiTokenAction.validateUserPermissions(List.of("cluster_monitor"), List.of());
}

@Test
public void testClusterPermissionsMorePermissableRegexThrowsException() {
when(privilegesEvaluator.mapRoles(null, null)).thenReturn(Set.of("cluster_monitor"));

assertThrows(OpenSearchException.class, () -> apiTokenAction.validateUserPermissions(List.of("*"), List.of()));
}

@Test
public void testClusterPermissionsFromMultipleRoles() throws Exception{
when(privilegesEvaluator.mapRoles(null, null)).thenReturn(Set.of("cluster_monitor", "read_group_logs-123"));

apiTokenAction.validateUserPermissions(List.of("cluster_monitor", "cluster_health"), List.of());
}

@Test
public void testAliasAllowsAccessOnUnderlyingIndices() throws Exception {
when(privilegesEvaluator.mapRoles(null, null)).thenReturn(Set.of("alias_group"));

ApiToken.IndexPermission requestedPerm = new ApiToken.IndexPermission(
List.of("my-index"),
List.of("read")
);

apiTokenAction.validateUserPermissions(List.of(), List.of(requestedPerm));
}
}

0 comments on commit c95d256

Please sign in to comment.