Skip to content
Closed
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,19 @@
import org.opensearch.common.util.concurrent.ThreadContext;
import org.opensearch.core.common.unit.ByteSizeUnit;
import org.opensearch.core.common.unit.ByteSizeValue;
import org.opensearch.security.action.apitokens.ApiToken;
import org.opensearch.security.action.apitokens.Permissions;
import org.opensearch.security.resolver.IndexResolverReplacer;
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.user.User;
import org.opensearch.security.util.MockIndexMetadataBuilder;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.opensearch.security.privileges.ActionPrivilegesTest.IndexPrivileges.IndicesAndAliases.resolved;
import static org.opensearch.security.privileges.PrivilegeEvaluatorResponseMatcher.isAllowed;
import static org.opensearch.security.privileges.PrivilegeEvaluatorResponseMatcher.isForbidden;
import static org.opensearch.security.privileges.PrivilegeEvaluatorResponseMatcher.isPartiallyOk;
Expand Down Expand Up @@ -280,6 +284,70 @@ public void hasAny_wildcard() throws Exception {
isForbidden(missingPrivileges("cluster:whatever"))
);
}

@Test
public void apiToken_explicit_failsWithWildcard() throws Exception {
SecurityDynamicConfiguration<RoleV7> roles = SecurityDynamicConfiguration.empty(CType.ROLES);
ActionPrivileges subject = new ActionPrivileges(roles, FlattenedActionGroups.EMPTY, null, Settings.EMPTY);
String token = "blah";
PermissionBasedPrivilegesEvaluationContext context = ctxForApiToken(
"apitoken:" + token,
new Permissions(List.of("*"), List.of())
);
// Explicit fails
assertThat(
subject.hasExplicitClusterPrivilege(context, "cluster:whatever"),
isForbidden(missingPrivileges("cluster:whatever"))
);
// Not explicit succeeds
assertThat(subject.hasClusterPrivilege(context, "cluster:whatever"), isAllowed());
// Any succeeds
assertThat(subject.hasAnyClusterPrivilege(context, ImmutableSet.of("cluster:whatever")), isAllowed());
}

@Test
public void apiToken_succeedsWithExactMatch() throws Exception {
SecurityDynamicConfiguration<RoleV7> roles = SecurityDynamicConfiguration.empty(CType.ROLES);
ActionPrivileges subject = new ActionPrivileges(roles, FlattenedActionGroups.EMPTY, null, Settings.EMPTY);
String token = "blah";
PermissionBasedPrivilegesEvaluationContext context = ctxForApiToken(
"apitoken:" + token,
new Permissions(List.of("cluster:whatever"), List.of())
);
// Explicit succeeds
assertThat(subject.hasExplicitClusterPrivilege(context, "cluster:whatever"), isAllowed());
// Not explicit succeeds
assertThat(subject.hasClusterPrivilege(context, "cluster:whatever"), isAllowed());
// Any succeeds
assertThat(subject.hasAnyClusterPrivilege(context, ImmutableSet.of("cluster:whatever")), isAllowed());
// Any succeeds
assertThat(subject.hasAnyClusterPrivilege(context, ImmutableSet.of("cluster:whatever", "cluster:other")), isAllowed());
}

@Test
public void apiToken_succeedsWithActionGroupsExapnded() throws Exception {
SecurityDynamicConfiguration<RoleV7> roles = SecurityDynamicConfiguration.empty(CType.ROLES);

SecurityDynamicConfiguration<ActionGroupsV7> config = SecurityDynamicConfiguration.fromYaml(
"CLUSTER_ALL:\n allowed_actions:\n - \"cluster:*\"",
CType.ACTIONGROUPS
);

FlattenedActionGroups actionGroups = new FlattenedActionGroups(config);
ActionPrivileges subject = new ActionPrivileges(roles, actionGroups, null, Settings.EMPTY);
String token = "blah";
PermissionBasedPrivilegesEvaluationContext context = ctxForApiToken(
"apitoken:" + token,
new Permissions(List.of("CLUSTER_ALL"), List.of())
);

// Explicit succeeds
assertThat(subject.hasExplicitClusterPrivilege(context, "cluster:whatever"), isAllowed());
// Not explicit succeeds
assertThat(subject.hasClusterPrivilege(context, "cluster:whatever"), isAllowed());
// Any succeeds
assertThat(subject.hasClusterPrivilege(context, "cluster:monitor/main"), isAllowed());
}
}

/**
Expand Down Expand Up @@ -314,9 +382,20 @@ public void positive_full() throws Exception {
assertThat(result, isAllowed());
}

@Test
public void apiTokens_positive_full() throws Exception {
String token = "blah";
PermissionBasedPrivilegesEvaluationContext context = ctxForApiToken(
"apitoken:" + token,
new Permissions(List.of("index_a11"), List.of(new ApiToken.IndexPermission(List.of("index_a11"), List.of("*"))))
);
PrivilegesEvaluatorResponse result = subject.hasIndexPrivilege(context, requiredActions, resolved("index_a11"));
assertThat(result, isAllowed());
}

@Test
public void positive_partial() throws Exception {
PrivilegesEvaluationContext ctx = ctx("test_role");
RoleBasedPrivilegesEvaluationContext ctx = ctx("test_role");
PrivilegesEvaluatorResponse result = subject.hasIndexPrivilege(ctx, requiredActions, resolved("index_a11", "index_a12"));

if (covers(ctx, "index_a11", "index_a12")) {
Expand All @@ -330,7 +409,7 @@ public void positive_partial() throws Exception {

@Test
public void positive_partial2() throws Exception {
PrivilegesEvaluationContext ctx = ctx("test_role");
RoleBasedPrivilegesEvaluationContext ctx = ctx("test_role");
PrivilegesEvaluatorResponse result = subject.hasIndexPrivilege(
ctx,
requiredActions,
Expand Down Expand Up @@ -363,14 +442,26 @@ public void positive_noLocal() throws Exception {

@Test
public void negative_wrongRole() throws Exception {
PrivilegesEvaluationContext ctx = ctx("other_role");
RoleBasedPrivilegesEvaluationContext ctx = ctx("other_role");
PrivilegesEvaluatorResponse result = subject.hasIndexPrivilege(ctx, requiredActions, resolved("index_a11"));
assertThat(result, isForbidden(missingPrivileges(requiredActions)));
}

@Test
public void apiToken_negative_noPermissions() throws Exception {
String token = "blah";
PermissionBasedPrivilegesEvaluationContext context = ctxForApiToken(
"apitoken:" + token,
new Permissions(List.of(), List.of(new ApiToken.IndexPermission(List.of(), List.of())))
);

PrivilegesEvaluatorResponse result = subject.hasIndexPrivilege(context, requiredActions, resolved("index_a11"));
assertThat(result, isForbidden());
}

@Test
public void negative_wrongAction() throws Exception {
PrivilegesEvaluationContext ctx = ctx("test_role");
RoleBasedPrivilegesEvaluationContext ctx = ctx("test_role");
PrivilegesEvaluatorResponse result = subject.hasIndexPrivilege(ctx, otherActions, resolved("index_a11"));

if (actionSpec.givenPrivs.contains("*")) {
Expand All @@ -382,7 +473,7 @@ public void negative_wrongAction() throws Exception {

@Test
public void positive_hasExplicit_full() {
PrivilegesEvaluationContext ctx = ctx("test_role");
RoleBasedPrivilegesEvaluationContext ctx = ctx("test_role");
PrivilegesEvaluatorResponse result = subject.hasExplicitIndexPrivilege(ctx, requiredActions, resolved("index_a11"));

if (actionSpec.givenPrivs.contains("*")) {
Expand All @@ -397,7 +488,21 @@ public void positive_hasExplicit_full() {
}
}

private boolean covers(PrivilegesEvaluationContext ctx, String... indices) {
@Test
public void apiTokens_positive_hasExplicit_full() {
String token = "blah";
PermissionBasedPrivilegesEvaluationContext context = ctxForApiToken(
"apitoken:" + token,
new Permissions(List.of("index_a11"), List.of(new ApiToken.IndexPermission(List.of("index_a11"), List.of("*"))))
);

PrivilegesEvaluatorResponse result = subject.hasExplicitIndexPrivilege(context, requiredActions, resolved("index_a11"));

assertThat(result, isForbidden());

}

private boolean covers(RoleBasedPrivilegesEvaluationContext ctx, String... indices) {
for (String index : indices) {
if (!indexSpec.covers(ctx.getUser(), index)) {
return false;
Expand Down Expand Up @@ -522,7 +627,7 @@ public static class DataStreams {

@Test
public void positive_full() throws Exception {
PrivilegesEvaluationContext ctx = ctx("test_role");
RoleBasedPrivilegesEvaluationContext ctx = ctx("test_role");
PrivilegesEvaluatorResponse result = subject.hasIndexPrivilege(ctx, requiredActions, resolved("data_stream_a11"));
if (covers(ctx, "data_stream_a11")) {
assertThat(result, isAllowed());
Expand All @@ -538,7 +643,7 @@ public void positive_full() throws Exception {

@Test
public void positive_partial() throws Exception {
PrivilegesEvaluationContext ctx = ctx("test_role");
RoleBasedPrivilegesEvaluationContext ctx = ctx("test_role");
PrivilegesEvaluatorResponse result = subject.hasIndexPrivilege(
ctx,
requiredActions,
Expand Down Expand Up @@ -569,19 +674,19 @@ public void positive_partial() throws Exception {

@Test
public void negative_wrongRole() throws Exception {
PrivilegesEvaluationContext ctx = ctx("other_role");
RoleBasedPrivilegesEvaluationContext ctx = ctx("other_role");
PrivilegesEvaluatorResponse result = subject.hasIndexPrivilege(ctx, requiredActions, resolved("data_stream_a11"));
assertThat(result, isForbidden(missingPrivileges(requiredActions)));
}

@Test
public void negative_wrongAction() throws Exception {
PrivilegesEvaluationContext ctx = ctx("test_role");
RoleBasedPrivilegesEvaluationContext ctx = ctx("test_role");
PrivilegesEvaluatorResponse result = subject.hasIndexPrivilege(ctx, otherActions, resolved("data_stream_a11"));
assertThat(result, isForbidden(missingPrivileges(otherActions)));
}

private boolean covers(PrivilegesEvaluationContext ctx, String... indices) {
private boolean covers(RoleBasedPrivilegesEvaluationContext ctx, String... indices) {
for (String index : indices) {
if (!indexSpec.covers(ctx.getUser(), index)) {
return false;
Expand Down Expand Up @@ -1039,10 +1144,14 @@ static SecurityDynamicConfiguration<RoleV7> createRoles(int numberOfRoles, int n
}
}

static PrivilegesEvaluationContext ctx(String... roles) {
User user = new User("test_user");
static RoleBasedPrivilegesEvaluationContext ctx(String... roles) {
return ctxWithUserName("test-user", roles);
}

static RoleBasedPrivilegesEvaluationContext ctxWithUserName(String userName, String... roles) {
User user = new User(userName);
user.addAttributes(ImmutableMap.of("attrs.dept_no", "a11"));
return new PrivilegesEvaluationContext(
return new RoleBasedPrivilegesEvaluationContext(
user,
ImmutableSet.copyOf(roles),
null,
Expand All @@ -1054,10 +1163,25 @@ static PrivilegesEvaluationContext ctx(String... roles) {
);
}

static PrivilegesEvaluationContext ctxByUsername(String username) {
static PermissionBasedPrivilegesEvaluationContext ctxForApiToken(String userName, Permissions permissions) {
User user = new User(userName);
user.addAttributes(ImmutableMap.of("attrs.dept_no", "a11"));
return new PermissionBasedPrivilegesEvaluationContext(
user,
null,
null,
null,
null,
new IndexNameExpressionResolver(new ThreadContext(Settings.EMPTY)),
null,
permissions
);
}

static RoleBasedPrivilegesEvaluationContext ctxByUsername(String username) {
User user = new User(username);
user.addAttributes(ImmutableMap.of("attrs.dept_no", "a11"));
return new PrivilegesEvaluationContext(
return new RoleBasedPrivilegesEvaluationContext(
user,
ImmutableSet.of(),
null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,14 +231,14 @@ public void equals() {
assertFalse(a1.equals(a1.toString()));
}

private static PrivilegesEvaluationContext ctx() {
private static RoleBasedPrivilegesEvaluationContext ctx() {
IndexNameExpressionResolver indexNameExpressionResolver = new IndexNameExpressionResolver(new ThreadContext(Settings.EMPTY));
IndexResolverReplacer indexResolverReplacer = new IndexResolverReplacer(indexNameExpressionResolver, () -> CLUSTER_STATE, null);
User user = new User("test_user");
user.addAttributes(ImmutableMap.of("attrs.a11", "a11"));
user.addAttributes(ImmutableMap.of("attrs.year", "year"));

return new PrivilegesEvaluationContext(
return new RoleBasedPrivilegesEvaluationContext(
user,
ImmutableSet.of(),
"indices:action/test",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,13 +188,13 @@ public void hasExplicitClusterPermissionPermissionForRestAdmin() {
.collect(Collectors.toList());
for (final Endpoint endpoint : noSslEndpoints) {
final String permission = ENDPOINTS_WITH_PERMISSIONS.get(endpoint).build();
final PrivilegesEvaluationContext ctx = ctx(restAdminApiRoleName(endpoint.name().toLowerCase(Locale.ROOT)));
final RoleBasedPrivilegesEvaluationContext ctx = ctx(restAdminApiRoleName(endpoint.name().toLowerCase(Locale.ROOT)));
Assert.assertTrue(endpoint.name(), actionPrivileges.hasExplicitClusterPrivilege(ctx, permission).isAllowed());
assertHasNoPermissionsForRestApiAdminOnePermissionRole(endpoint, ctx);
}
// verify SSL endpoint with 2 actions
for (final String sslAction : ImmutableSet.of(CERTS_INFO_ACTION, RELOAD_CERTS_ACTION)) {
final PrivilegesEvaluationContext ctx = ctx(restAdminApiRoleName(sslAction));
final RoleBasedPrivilegesEvaluationContext ctx = ctx(restAdminApiRoleName(sslAction));
final PermissionBuilder permissionBuilder = ENDPOINTS_WITH_PERMISSIONS.get(Endpoint.SSL);
Assert.assertTrue(
Endpoint.SSL + "/" + sslAction,
Expand All @@ -203,7 +203,7 @@ public void hasExplicitClusterPermissionPermissionForRestAdmin() {
assertHasNoPermissionsForRestApiAdminOnePermissionRole(Endpoint.SSL, ctx);
}
// verify CONFIG endpoint with 1 action
final PrivilegesEvaluationContext ctx = ctx(restAdminApiRoleName(SECURITY_CONFIG_UPDATE));
final RoleBasedPrivilegesEvaluationContext ctx = ctx(restAdminApiRoleName(SECURITY_CONFIG_UPDATE));
final PermissionBuilder permissionBuilder = ENDPOINTS_WITH_PERMISSIONS.get(Endpoint.CONFIG);
Assert.assertTrue(
Endpoint.SSL + "/" + SECURITY_CONFIG_UPDATE,
Expand All @@ -212,7 +212,10 @@ public void hasExplicitClusterPermissionPermissionForRestAdmin() {
assertHasNoPermissionsForRestApiAdminOnePermissionRole(Endpoint.CONFIG, ctx);
}

void assertHasNoPermissionsForRestApiAdminOnePermissionRole(final Endpoint allowEndpoint, final PrivilegesEvaluationContext ctx) {
void assertHasNoPermissionsForRestApiAdminOnePermissionRole(
final Endpoint allowEndpoint,
final RoleBasedPrivilegesEvaluationContext ctx
) {
final Collection<Endpoint> noPermissionEndpoints = ENDPOINTS_WITH_PERMISSIONS.keySet()
.stream()
.filter(e -> e != allowEndpoint)
Expand Down Expand Up @@ -250,8 +253,17 @@ static SecurityDynamicConfiguration<RoleV7> createRolesConfig() throws IOExcepti
return SecurityDynamicConfiguration.fromNode(rolesNode, CType.ROLES, 2, 0, 0);
}

static PrivilegesEvaluationContext ctx(String... roles) {
return new PrivilegesEvaluationContext(new User("test_user"), ImmutableSet.copyOf(roles), null, null, null, null, null, null);
static RoleBasedPrivilegesEvaluationContext ctx(String... roles) {
return new RoleBasedPrivilegesEvaluationContext(
new User("test_user"),
ImmutableSet.copyOf(roles),
null,
null,
null,
null,
null,
null
);
}

}
Loading
Loading