diff --git a/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java b/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java index 1e56b1717cd..85fac3c9543 100644 --- a/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java +++ b/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java @@ -53,6 +53,7 @@ import org.elasticsearch.xpack.security.user.XPackUser; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -308,19 +309,20 @@ public class AuthorizationService extends AbstractComponent { } private GlobalPermission permission(String[] roleNames) { + final String[] anonymousRoles = isAnonymousEnabled ? anonymousUser.roles() : Strings.EMPTY_ARRAY; if (roleNames.length == 0) { - return DefaultRole.INSTANCE; - } - - if (roleNames.length == 1) { - Role role = rolesStore.role(roleNames[0]); - return role == null ? DefaultRole.INSTANCE : GlobalPermission.Compound.builder().add(DefaultRole.INSTANCE).add(role).build(); + if (anonymousRoles.length == 0) { + assert isAnonymousEnabled == false : "anonymous is only enabled when the anonymous user has roles"; + return DefaultRole.INSTANCE; + } } // we'll take all the roles and combine their associated permissions + final Set uniqueNames = new HashSet<>(Arrays.asList(roleNames)); + uniqueNames.addAll(Arrays.asList(anonymousRoles)); GlobalPermission.Compound.Builder roles = GlobalPermission.Compound.builder().add(DefaultRole.INSTANCE); - for (String roleName : roleNames) { + for (String roleName : uniqueNames) { Role role = rolesStore.role(roleName); if (role != null) { roles.add(role); diff --git a/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authz/store/NativeRolesStore.java b/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authz/store/NativeRolesStore.java index 75e4c3df5d0..1d25ffaf8c0 100644 --- a/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authz/store/NativeRolesStore.java +++ b/elasticsearch/src/main/java/org/elasticsearch/xpack/security/authz/store/NativeRolesStore.java @@ -354,6 +354,9 @@ public class NativeRolesStore extends AbstractComponent implements RolesStore, C @Override public Role role(String roleName) { + if (state() != State.STARTED) { + return null; + } RoleAndVersion roleAndVersion = getRoleAndVersion(roleName); return roleAndVersion == null ? null : roleAndVersion.getRole(); } @@ -443,6 +446,10 @@ public class NativeRolesStore extends AbstractComponent implements RolesStore, C } private RoleAndVersion getRoleAndVersion(final String roleId) { + if (securityIndexExists == false) { + return null; + } + RoleAndVersion roleAndVersion = null; final AtomicReference getRef = new AtomicReference<>(null); final CountDownLatch latch = new CountDownLatch(1); diff --git a/elasticsearch/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmIntegTests.java b/elasticsearch/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmIntegTests.java index 45540150e92..06ae853661f 100644 --- a/elasticsearch/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmIntegTests.java +++ b/elasticsearch/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealmIntegTests.java @@ -49,6 +49,7 @@ import java.util.List; import java.util.Map; import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertNoTimeout; import static org.elasticsearch.xpack.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.containsString; @@ -316,10 +317,12 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase { .addIndices(new String[]{"*"}, new String[]{"read"}, new FieldPermissions(new String[]{"body", "title"}, null), new BytesArray("{\"match_all\": {}}")) .get(); - try { - client().filterWithHeader(Collections.singletonMap("Authorization", token)).admin().cluster().prepareHealth().get(); - fail("user should not be able to execute any cluster actions!"); - } catch (ElasticsearchSecurityException e) { + if (anonymousEnabled) { + assertNoTimeout(client() + .filterWithHeader(Collections.singletonMap("Authorization", token)).admin().cluster().prepareHealth().get()); + } else { + ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class, () -> client() + .filterWithHeader(Collections.singletonMap("Authorization", token)).admin().cluster().prepareHealth().get()); assertThat(e.status(), is(RestStatus.FORBIDDEN)); } } else { @@ -358,10 +361,12 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase { .prepareHealth().get(); assertFalse(response.isTimedOut()); c.prepareDeleteRole("test_role").get(); - try { - client().filterWithHeader(Collections.singletonMap("Authorization", token)).admin().cluster().prepareHealth().get(); - fail("user should not be able to execute any actions!"); - } catch (ElasticsearchSecurityException e) { + if (anonymousEnabled) { + assertNoTimeout( + client().filterWithHeader(Collections.singletonMap("Authorization", token)).admin().cluster().prepareHealth().get()); + } else { + ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class, () -> + client().filterWithHeader(Collections.singletonMap("Authorization", token)).admin().cluster().prepareHealth().get()); assertThat(e.status(), is(RestStatus.FORBIDDEN)); } } @@ -406,11 +411,14 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase { assertThat(joe.roles(), arrayContaining("read_role")); assertThat(joe.fullName(), is("Joe Smith")); - // test that role change took effect - try { - client().filterWithHeader(Collections.singletonMap("Authorization", token)).admin().cluster().prepareHealth().get(); - fail("test_role does not have permission to get health"); - } catch (ElasticsearchSecurityException e) { + // test that role change took effect if anonymous is disabled as anonymous grants monitoring permissions... + if (anonymousEnabled) { + assertNoTimeout( + client().filterWithHeader(Collections.singletonMap("Authorization", token)).admin().cluster().prepareHealth().get()); + } else { + ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class, () -> + client().filterWithHeader(Collections.singletonMap("Authorization", token)).admin().cluster().prepareHealth().get()); + assertThat(e.status(), is(RestStatus.FORBIDDEN)); assertThat(e.getMessage(), containsString("authorized")); } @@ -434,7 +442,8 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase { // test with new password and role response = client() .filterWithHeader( - Collections.singletonMap("Authorization",basicAuthHeaderValue("joe", new SecuredString("changeme2".toCharArray())))) + Collections.singletonMap("Authorization", + basicAuthHeaderValue("joe", new SecuredString("changeme2".toCharArray())))) .admin().cluster().prepareHealth().get(); assertFalse(response.isTimedOut()); } @@ -558,7 +567,8 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase { response = client() .filterWithHeader( - Collections.singletonMap("Authorization", basicAuthHeaderValue("joe", new SecuredString("changeme".toCharArray())))) + Collections.singletonMap("Authorization", + basicAuthHeaderValue("joe", new SecuredString("changeme".toCharArray())))) .admin().cluster().prepareHealth().get(); assertThat(response.isTimedOut(), is(false)); } diff --git a/elasticsearch/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java b/elasticsearch/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java index 5a8888240f9..356cd4192b5 100644 --- a/elasticsearch/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java +++ b/elasticsearch/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java @@ -15,6 +15,7 @@ import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest; import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest.AliasActions; import org.elasticsearch.action.admin.indices.create.CreateIndexAction; import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; +import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsAction; import org.elasticsearch.action.admin.indices.exists.indices.IndicesExistsRequest; import org.elasticsearch.action.admin.indices.recovery.RecoveryAction; import org.elasticsearch.action.admin.indices.recovery.RecoveryRequest; @@ -614,6 +615,32 @@ public class AuthorizationServiceTests extends ESTestCase { } } + public void testAnonymousRolesAreAppliedToOtherUsers() { + TransportRequest request = new ClusterHealthRequest(); + ClusterState state = mock(ClusterState.class); + Settings settings = Settings.builder().put(AnonymousUser.ROLES_SETTING.getKey(), "anonymous_user_role").build(); + final AnonymousUser anonymousUser = new AnonymousUser(settings); + authorizationService = new AuthorizationService(settings, rolesStore, clusterService, auditTrail, + new DefaultAuthenticationFailureHandler(), threadPool, anonymousUser); + + when(rolesStore.role("anonymous_user_role")) + .thenReturn(Role.builder("anonymous_user_role") + .cluster(ClusterPrivilege.ALL) + .add(IndexPrivilege.ALL, "a") + .build()); + when(clusterService.state()).thenReturn(state); + when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA); + + // sanity check the anonymous user + authorizationService.authorize(createAuthentication(anonymousUser), ClusterHealthAction.NAME, request); + authorizationService.authorize(createAuthentication(anonymousUser), IndicesExistsAction.NAME, new IndicesExistsRequest("a")); + + // test the no role user + final User userWithNoRoles = new User("no role user"); + authorizationService.authorize(createAuthentication(userWithNoRoles), ClusterHealthAction.NAME, request); + authorizationService.authorize(createAuthentication(userWithNoRoles), IndicesExistsAction.NAME, new IndicesExistsRequest("a")); + } + private Authentication createAuthentication(User user) { RealmRef lookedUpBy = user.runAs() == null ? null : new RealmRef("looked", "up", "by"); return new Authentication(user, new RealmRef("test", "test", "foo"), lookedUpBy);