From bf268c59f39e048368d54fbdd7fb8ca7070588a3 Mon Sep 17 00:00:00 2001 From: Yang Wang Date: Tue, 9 Jun 2020 12:01:15 +1000 Subject: [PATCH] Revert "Resolve anonymous roles and deduplicate roles during authentication (#53453)" (#57853) This reverts commit 3fa765aafcf3f854c8dc085d20dfb45dae8623d7. --- .../action/user/AuthenticateResponse.java | 2 +- .../xpack/core/security/user/User.java | 4 -- .../EnableSecurityOnBasicLicenseIT.java | 4 +- .../security/authc/AuthenticationService.java | 33 +------------ .../security/authc/esnative/NativeRealm.java | 10 ++-- .../authc/esnative/NativeUsersStore.java | 3 +- .../authc/file/FileUserRolesStore.java | 3 +- .../security/authz/AuthorizationService.java | 11 ++++- .../authz/store/CompositeRolesStore.java | 12 +++++ .../authc/AuthenticationServiceTests.java | 46 ------------------- .../authc/esnative/NativeUsersStoreTests.java | 16 ------- .../authz/store/CompositeRolesStoreTests.java | 39 ++++++++++++++++ .../action/RestAuthenticateActionTests.java | 13 ++---- .../xpack/security/authc/file/users_roles | 4 +- 14 files changed, 75 insertions(+), 125 deletions(-) diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/user/AuthenticateResponse.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/user/AuthenticateResponse.java index a74255e6d2022..36faef7569794 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/user/AuthenticateResponse.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/action/user/AuthenticateResponse.java @@ -34,4 +34,4 @@ public void writeTo(StreamOutput out) throws IOException { authentication.writeTo(out); } -} + } diff --git a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/User.java b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/User.java index d9399d5072111..12cb2f6bc7ce0 100644 --- a/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/User.java +++ b/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/user/User.java @@ -118,10 +118,6 @@ public boolean isRunAs() { return authenticatedUser != null; } - public User withRoles(String[] newRoles) { - return new User(username, newRoles, fullName, email, metadata, enabled); - } - @Override public String toString() { StringBuilder sb = new StringBuilder(); diff --git a/x-pack/plugin/security/qa/basic-enable-security/src/test/java/org/elasticsearch/xpack/security/EnableSecurityOnBasicLicenseIT.java b/x-pack/plugin/security/qa/basic-enable-security/src/test/java/org/elasticsearch/xpack/security/EnableSecurityOnBasicLicenseIT.java index 5f8b2f9469e81..fa64a89f2f633 100644 --- a/x-pack/plugin/security/qa/basic-enable-security/src/test/java/org/elasticsearch/xpack/security/EnableSecurityOnBasicLicenseIT.java +++ b/x-pack/plugin/security/qa/basic-enable-security/src/test/java/org/elasticsearch/xpack/security/EnableSecurityOnBasicLicenseIT.java @@ -24,7 +24,7 @@ import java.util.Map; import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; -import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.notNullValue; @@ -126,7 +126,7 @@ private void checkAuthentication() throws IOException { final Map auth = getAsMap("/_security/_authenticate"); // From file realm, configured in build.gradle assertThat(ObjectPath.evaluate(auth, "username"), equalTo("security_test_user")); - assertThat(ObjectPath.evaluate(auth, "roles"), containsInAnyOrder("security_test_role", "anonymous")); + assertThat(ObjectPath.evaluate(auth, "roles"), contains("security_test_role")); } private void checkAllowedWrite(String indexName) throws IOException { diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/AuthenticationService.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/AuthenticationService.java index 6bdb5f1f74eca..e93336133e5bf 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/AuthenticationService.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/AuthenticationService.java @@ -49,14 +49,11 @@ import org.elasticsearch.xpack.security.support.SecurityIndexManager; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -669,8 +666,7 @@ void finishAuthentication(User finalUser) { logger.debug("user [{}] is disabled. failing authentication", finalUser); listener.onFailure(request.authenticationFailed(authenticationToken)); } else { - final Authentication finalAuth = new Authentication( - maybeConsolidateRolesForUser(finalUser), authenticatedBy, lookedupBy); + final Authentication finalAuth = new Authentication(finalUser, authenticatedBy, lookedupBy); writeAuthToContext(finalAuth); } } @@ -703,33 +699,6 @@ void writeAuthToContext(Authentication authentication) { private void authenticateToken(AuthenticationToken token) { this.consumeToken(token); } - - private User maybeConsolidateRolesForUser(User user) { - if (User.isInternal(user)) { - return user; - } else if (isAnonymousUserEnabled && anonymousUser.equals(user) == false) { - if (anonymousUser.roles().length == 0) { - throw new IllegalStateException("anonymous is only enabled when the anonymous user has roles"); - } - User userWithMergedRoles = user.withRoles(mergeRoles(user.roles(), anonymousUser.roles())); - if (user.isRunAs()) { - final User authUserWithMergedRoles = user.authenticatedUser().withRoles( - mergeRoles(user.authenticatedUser().roles(), anonymousUser.roles())); - userWithMergedRoles = new User(userWithMergedRoles, authUserWithMergedRoles); - } - return userWithMergedRoles; - } else { - return user; - } - } - - private String[] mergeRoles(String[] existingRoles, String[] otherRoles) { - Set roles = new LinkedHashSet<>(Arrays.asList(existingRoles)); - if (otherRoles != null) { - Collections.addAll(roles, otherRoles); - } - return roles.toArray(new String[0]); - } } abstract static class AuditableRequest { diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealm.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealm.java index c8ecc4ce70bd2..53e171ff4f321 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealm.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/NativeRealm.java @@ -24,21 +24,21 @@ */ public class NativeRealm extends CachingUsernamePasswordRealm { - private final NativeUsersStore usersStore; + private final NativeUsersStore userStore; public NativeRealm(RealmConfig config, NativeUsersStore usersStore, ThreadPool threadPool) { super(config, threadPool); - this.usersStore = usersStore; + this.userStore = usersStore; } @Override protected void doLookupUser(String username, ActionListener listener) { - usersStore.getUser(username, listener); + userStore.getUser(username, listener); } @Override protected void doAuthenticate(UsernamePasswordToken token, ActionListener listener) { - usersStore.verifyPassword(token.principal(), token.credentials(), listener); + userStore.verifyPassword(token.principal(), token.credentials(), listener); } public void onSecurityIndexStateChange(SecurityIndexManager.State previousState, SecurityIndexManager.State currentState) { @@ -50,7 +50,7 @@ public void onSecurityIndexStateChange(SecurityIndexManager.State previousState, @Override public void usageStats(ActionListener> listener) { super.usageStats(ActionListener.wrap(stats -> - usersStore.getUserCount(ActionListener.wrap(size -> { + userStore.getUserCount(ActionListener.wrap(size -> { stats.put("size", size); listener.onResponse(stats); }, listener::onFailure)) diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/NativeUsersStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/NativeUsersStore.java index 1516d1f7346be..79d537048be40 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/NativeUsersStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/esnative/NativeUsersStore.java @@ -57,7 +57,6 @@ import java.util.Collection; import java.util.Collections; import java.util.HashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.function.Consumer; @@ -644,7 +643,7 @@ private UserAndPassword transformUser(final String id, final Map final String username = id.substring(USER_DOC_TYPE.length() + 1); try { String password = (String) sourceMap.get(Fields.PASSWORD.getPreferredName()); - String[] roles = new LinkedHashSet<>((List)sourceMap.get(Fields.ROLES.getPreferredName())).toArray(Strings.EMPTY_ARRAY); + String[] roles = ((List) sourceMap.get(Fields.ROLES.getPreferredName())).toArray(Strings.EMPTY_ARRAY); String fullName = (String) sourceMap.get(Fields.FULL_NAME.getPreferredName()); String email = (String) sourceMap.get(Fields.EMAIL.getPreferredName()); Boolean enabled = (Boolean) sourceMap.get(Fields.ENABLED.getPreferredName()); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/FileUserRolesStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/FileUserRolesStore.java index b91852bc9c68b..1eb2e468c8ef2 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/FileUserRolesStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/FileUserRolesStore.java @@ -30,7 +30,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Map; @@ -165,7 +164,7 @@ public static Map parseFile(Path path, @Nullable Logger logger Map usersRoles = new HashMap<>(); for (Map.Entry> entry : userToRoles.entrySet()) { - usersRoles.put(entry.getKey(), new LinkedHashSet<>(entry.getValue()).toArray(new String[0])); + usersRoles.put(entry.getKey(), entry.getValue().toArray(new String[entry.getValue().size()])); } logger.debug("parsed [{}] user to role mappings from file [{}]", usersRoles.size(), path.toAbsolutePath()); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java index 26b07beb4838d..e2a335614eecb 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java @@ -60,8 +60,11 @@ import org.elasticsearch.xpack.core.security.authz.privilege.ClusterPrivilegeResolver; import org.elasticsearch.xpack.core.security.authz.privilege.IndexPrivilege; import org.elasticsearch.xpack.core.security.user.AnonymousUser; +import org.elasticsearch.xpack.core.security.user.AsyncSearchUser; import org.elasticsearch.xpack.core.security.user.SystemUser; import org.elasticsearch.xpack.core.security.user.User; +import org.elasticsearch.xpack.core.security.user.XPackSecurityUser; +import org.elasticsearch.xpack.core.security.user.XPackUser; import org.elasticsearch.xpack.security.audit.AuditLevel; import org.elasticsearch.xpack.security.audit.AuditTrail; import org.elasticsearch.xpack.security.audit.AuditTrailService; @@ -170,7 +173,7 @@ public void authorize(final Authentication authentication, final String action, if (auditId == null) { // We would like to assert that there is an existing request-id, but if this is a system action, then that might not be // true because the request-id is generated during authentication - if (User.isInternal(authentication.getUser()) != false) { + if (isInternalUser(authentication.getUser()) != false) { auditId = AuditUtil.getOrGenerateRequestId(threadContext); } else { auditTrailService.get().tamperedRequest(null, authentication.getUser(), action, originalRequest); @@ -365,7 +368,7 @@ AuthorizationEngine getAuthorizationEngine(final Authentication authentication) private AuthorizationEngine getAuthorizationEngineForUser(final User user) { if (rbacEngine != authorizationEngine && licenseState.isSecurityEnabled() && licenseState.isAllowed(Feature.SECURITY_AUTHORIZATION_ENGINE)) { - if (ClientReservedRealm.isReserved(user.principal(), settings) || User.isInternal(user)) { + if (ClientReservedRealm.isReserved(user.principal(), settings) || isInternalUser(user)) { return rbacEngine; } else { return authorizationEngine; @@ -416,6 +419,10 @@ private TransportRequest maybeUnwrapRequest(Authentication authentication, Trans return request; } + private boolean isInternalUser(User user) { + return SystemUser.is(user) || XPackUser.is(user) || XPackSecurityUser.is(user) || AsyncSearchUser.is(user); + } + private void authorizeRunAs(final RequestInfo requestInfo, final AuthorizationInfo authzInfo, final ActionListener listener) { final Authentication authentication = requestInfo.getAuthentication(); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java index 4bb8f31f0bcbf..f10c2eb247100 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStore.java @@ -44,6 +44,7 @@ import org.elasticsearch.xpack.core.security.authz.store.RoleRetrievalResult; import org.elasticsearch.xpack.core.security.support.CacheIteratorHelper; import org.elasticsearch.xpack.core.security.support.MetadataUtils; +import org.elasticsearch.xpack.core.security.user.AnonymousUser; import org.elasticsearch.xpack.core.security.user.AsyncSearchUser; import org.elasticsearch.xpack.core.security.user.SystemUser; import org.elasticsearch.xpack.core.security.user.User; @@ -100,7 +101,9 @@ public class CompositeRolesStore { private final DocumentSubsetBitsetCache dlsBitsetCache; private final ThreadContext threadContext; private final AtomicLong numInvalidation = new AtomicLong(); + private final AnonymousUser anonymousUser; private final ApiKeyService apiKeyService; + private final boolean isAnonymousEnabled; private final List, ActionListener>> builtInRoleProviders; private final List, ActionListener>> allRoleProviders; @@ -143,6 +146,8 @@ public CompositeRolesStore(Settings settings, FileRolesStore fileRolesStore, Nat allList.addAll(rolesProviders); this.allRoleProviders = Collections.unmodifiableList(allList); } + this.anonymousUser = new AnonymousUser(settings); + this.isAnonymousEnabled = AnonymousUser.isAnonymousEnabled(settings); } public void roles(Set roleNames, ActionListener roleActionListener) { @@ -232,6 +237,13 @@ public void getRoles(User user, Authentication authentication, ActionListener roleNames = new HashSet<>(Arrays.asList(user.roles())); + if (isAnonymousEnabled && anonymousUser.equals(user) == false) { + if (anonymousUser.roles().length == 0) { + throw new IllegalStateException("anonymous is only enabled when the anonymous user has roles"); + } + Collections.addAll(roleNames, anonymousUser.roles()); + } + if (roleNames.isEmpty()) { roleActionListener.onResponse(Role.EMPTY); } else if (roleNames.contains(ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR.getName())) { diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java index 9414ea7df70f3..d1330740d314e 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/AuthenticationServiceTests.java @@ -74,11 +74,8 @@ import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.EmptyAuthorizationInfo; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; import org.elasticsearch.xpack.core.security.user.AnonymousUser; -import org.elasticsearch.xpack.core.security.user.AsyncSearchUser; import org.elasticsearch.xpack.core.security.user.SystemUser; import org.elasticsearch.xpack.core.security.user.User; -import org.elasticsearch.xpack.core.security.user.XPackSecurityUser; -import org.elasticsearch.xpack.core.security.user.XPackUser; import org.elasticsearch.xpack.security.audit.AuditTrail; import org.elasticsearch.xpack.security.audit.AuditTrailService; import org.elasticsearch.xpack.security.audit.AuditUtil; @@ -111,7 +108,6 @@ import static org.elasticsearch.xpack.core.security.support.Exceptions.authenticationError; import static org.elasticsearch.xpack.security.authc.TokenServiceTests.mockGetTokenFromId; import static org.hamcrest.Matchers.arrayContaining; -import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.emptyOrNullString; @@ -906,48 +902,6 @@ public void testAnonymousUserTransportWithDefaultUser() throws Exception { assertThreadContextContainsAuthentication(result); } - public void testInheritAnonymousUserRoles() { - Settings settings = Settings.builder() - .putList(AnonymousUser.ROLES_SETTING.getKey(), "r3", "r4", "r5") - .build(); - final AnonymousUser anonymousUser = new AnonymousUser(settings); - service = new AuthenticationService(settings, realms, auditTrailService, - new DefaultAuthenticationFailureHandler(Collections.emptyMap()), - threadPool, anonymousUser, tokenService, apiKeyService); - User user1 = new User("username", "r1", "r2", "r3"); - when(firstRealm.token(threadContext)).thenReturn(token); - when(firstRealm.supports(token)).thenReturn(true); - mockAuthenticate(firstRealm, token, user1); - // this call does not actually go async - final AtomicBoolean completed = new AtomicBoolean(false); - service.authenticate(restRequest, true, ActionListener.wrap(authentication -> { - assertThat(authentication.getUser().roles(), arrayContainingInAnyOrder("r1", "r2", "r3", "r4", "r5")); - setCompletedToTrue(completed); - }, this::logAndFail)); - assertTrue(completed.get()); - } - - public void testSystemUsersDoNotInheritAnonymousRoles() { - Settings settings = Settings.builder() - .putList(AnonymousUser.ROLES_SETTING.getKey(), "r3", "r4", "r5") - .build(); - final AnonymousUser anonymousUser = new AnonymousUser(settings); - service = new AuthenticationService(settings, realms, auditTrailService, - new DefaultAuthenticationFailureHandler(Collections.emptyMap()), - threadPool, anonymousUser, tokenService, apiKeyService); - when(firstRealm.token(threadContext)).thenReturn(token); - when(firstRealm.supports(token)).thenReturn(true); - final User sysUser = randomFrom(SystemUser.INSTANCE, XPackUser.INSTANCE, XPackSecurityUser.INSTANCE, AsyncSearchUser.INSTANCE); - mockAuthenticate(firstRealm, token, sysUser); - // this call does not actually go async - final AtomicBoolean completed = new AtomicBoolean(false); - service.authenticate(restRequest, true, ActionListener.wrap(authentication -> { - assertThat(authentication.getUser().roles(), equalTo(sysUser.roles())); - setCompletedToTrue(completed); - }, this::logAndFail)); - assertTrue(completed.get()); - } - public void testRealmTokenThrowingException() throws Exception { final String reqId = AuditUtil.getOrGenerateRequestId(threadContext); when(firstRealm.token(threadContext)).thenThrow(authenticationError("realm doesn't like tokens")); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeUsersStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeUsersStoreTests.java index 7875b2ef74284..35a5f6d8c58ba 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeUsersStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/esnative/NativeUsersStoreTests.java @@ -52,7 +52,6 @@ import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -155,21 +154,6 @@ public void testVerifyUserWithCorrectPassword() throws Exception { } public void testVerifyUserWithIncorrectPassword() throws Exception { - final NativeUsersStore nativeUsersStore = startNativeUsersStore(); - final String username = randomAlphaOfLengthBetween(4, 12); - final SecureString password = new SecureString(randomAlphaOfLengthBetween(12, 16).toCharArray()); - final List roles = randomList(1, 4, () -> randomAlphaOfLength(12)); - roles.add(randomIntBetween(0, roles.size()), roles.get(0)); - - final PlainActionFuture future = new PlainActionFuture<>(); - nativeUsersStore.verifyPassword(username, password, future); - respondToGetUserRequest(username, password, roles.toArray(new String[0])); - - final AuthenticationResult result = future.get(); - assertThat(result.getUser().roles(), arrayContainingInAnyOrder(roles.stream().distinct().toArray())); - } - - public void testDeduplicateUserRoles() throws Exception { final NativeUsersStore nativeUsersStore = startNativeUsersStore(); final String username = randomAlphaOfLengthBetween(4, 12); final SecureString correctPassword = new SecureString(randomAlphaOfLengthBetween(12, 16).toCharArray()); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java index 1de0659a496fd..ff37a26132a08 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/store/CompositeRolesStoreTests.java @@ -56,6 +56,7 @@ import org.elasticsearch.xpack.core.security.authz.store.RoleRetrievalResult; import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames; import org.elasticsearch.xpack.core.security.support.MetadataUtils; +import org.elasticsearch.xpack.core.security.user.AnonymousUser; import org.elasticsearch.xpack.core.security.user.AsyncSearchUser; import org.elasticsearch.xpack.core.security.user.SystemUser; import org.elasticsearch.xpack.core.security.user.User; @@ -90,6 +91,7 @@ import static org.hamcrest.Matchers.anyOf; import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; @@ -902,6 +904,43 @@ nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), null, mo assertEquals(Role.EMPTY, roles); } + public void testAnonymousUserEnabledRoleAdded() { + Settings settings = Settings.builder() + .put(SECURITY_ENABLED_SETTINGS) + .put(AnonymousUser.ROLES_SETTING.getKey(), "anonymous_user_role") + .build(); + final FileRolesStore fileRolesStore = mock(FileRolesStore.class); + doCallRealMethod().when(fileRolesStore).accept(any(Set.class), any(ActionListener.class)); + final NativeRolesStore nativeRolesStore = mock(NativeRolesStore.class); + doCallRealMethod().when(nativeRolesStore).accept(any(Set.class), any(ActionListener.class)); + doAnswer(invocationOnMock -> { + Set names = (Set) invocationOnMock.getArguments()[0]; + if (names.size() == 1 && names.contains("anonymous_user_role")) { + RoleDescriptor rd = new RoleDescriptor("anonymous_user_role", null, null, null); + return Collections.singleton(rd); + } + return Collections.emptySet(); + }). + when(fileRolesStore).roleDescriptors(anySetOf(String.class)); + doAnswer((invocationOnMock) -> { + ActionListener callback = (ActionListener) invocationOnMock.getArguments()[1]; + callback.onResponse(RoleRetrievalResult.failure(new RuntimeException("intentionally failed!"))); + return null; + }).when(nativeRolesStore).getRoleDescriptors(isA(Set.class), any(ActionListener.class)); + final ReservedRolesStore reservedRolesStore = spy(new ReservedRolesStore()); + + final CompositeRolesStore compositeRolesStore = buildCompositeRolesStore(settings, fileRolesStore, nativeRolesStore, + reservedRolesStore, mock(NativePrivilegeStore.class), null, mock(ApiKeyService.class), null, null); + verify(fileRolesStore).addListener(any(Consumer.class)); // adds a listener in ctor + + PlainActionFuture rolesFuture = new PlainActionFuture<>(); + final User user = new User("no role user"); + Authentication auth = new Authentication(user, new RealmRef("name", "type", "node"), null); + compositeRolesStore.getRoles(user, auth, rolesFuture); + final Role roles = rolesFuture.actionGet(); + assertThat(Arrays.asList(roles.names()), hasItem("anonymous_user_role")); + } + public void testDoesNotUseRolesStoreForXPacAndAsyncSearchUser() { final FileRolesStore fileRolesStore = mock(FileRolesStore.class); doCallRealMethod().when(fileRolesStore).accept(any(Set.class), any(ActionListener.class)); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateActionTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateActionTests.java index 7acdf313a53c6..1e7955175f6d2 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateActionTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/rest/action/RestAuthenticateActionTests.java @@ -23,7 +23,6 @@ import static org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken.basicAuthHeaderValue; import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.containsInAnyOrder; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -67,14 +66,8 @@ public void testAuthenticateApi() throws Exception { assertThat(objectPath.evaluate("lookup_realm.name").toString(), equalTo("file")); assertThat(objectPath.evaluate("lookup_realm.type").toString(), equalTo("file")); List roles = objectPath.evaluate("roles"); - - if (anonymousEnabled) { - assertThat(roles.size(), is(2)); - assertThat(roles, containsInAnyOrder(SecuritySettingsSource.TEST_ROLE, "foo")); - } else { - assertThat(roles.size(), is(1)); - assertThat(roles, contains(SecuritySettingsSource.TEST_ROLE)); - } + assertThat(roles.size(), is(1)); + assertThat(roles, contains(SecuritySettingsSource.TEST_ROLE)); } public void testAuthenticateApiWithoutAuthentication() throws Exception { @@ -87,7 +80,7 @@ public void testAuthenticateApiWithoutAuthentication() throws Exception { @SuppressWarnings("unchecked") List roles = (List) objectPath.evaluate("roles"); assertThat(roles.size(), is(2)); - assertThat(roles, containsInAnyOrder(SecuritySettingsSource.TEST_ROLE, "foo")); + assertThat(roles, contains(SecuritySettingsSource.TEST_ROLE, "foo")); } else { fail("request should have failed"); } diff --git a/x-pack/plugin/security/src/test/resources/org/elasticsearch/xpack/security/authc/file/users_roles b/x-pack/plugin/security/src/test/resources/org/elasticsearch/xpack/security/authc/file/users_roles index cdd902de013a3..390128c14e889 100644 --- a/x-pack/plugin/security/src/test/resources/org/elasticsearch/xpack/security/authc/file/users_roles +++ b/x-pack/plugin/security/src/test/resources/org/elasticsearch/xpack/security/authc/file/users_roles @@ -4,6 +4,4 @@ role2: user1,user2 role3: user1, user2 , user3 role4: period.user # another comment line -# and another one -# Test duplicated roles -role1: user1 +# and another one \ No newline at end of file