From 95e167b9071dd7804df5dfb651c29006cff0eca1 Mon Sep 17 00:00:00 2001 From: jaymode Date: Fri, 7 Dec 2018 07:55:01 -0700 Subject: [PATCH 1/3] Enable caching of roles from the api keys This commit enables api keys to share the same cache for roles that is already in use for roles from other sources. In order to avoid the possibility of a key collision with roles that do not belong to api keys, the key for the roles cache now includes a source field that prevents these collisions. --- .../xpack/security/Security.java | 5 +- .../xpack/security/authc/ApiKeyService.java | 24 +--- .../security/authz/AuthorizationService.java | 9 +- .../authz/store/CompositeRolesStore.java | 121 ++++++++++++------ .../security/authc/ApiKeyServiceTests.java | 38 +----- .../authz/AuthorizationServiceTests.java | 25 ++-- .../authz/IndicesAndAliasesResolverTests.java | 4 +- .../authz/store/CompositeRolesStoreTests.java | 74 +++++------ 8 files changed, 143 insertions(+), 157 deletions(-) diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java index 3f567abb5701e..4b4c33c6bea5f 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/Security.java @@ -465,6 +465,7 @@ Collection createComponents(Client client, ThreadPool threadPool, Cluste final NativePrivilegeStore privilegeStore = new NativePrivilegeStore(settings, client, securityIndex.get()); components.add(privilegeStore); + final FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(settings); final FileRolesStore fileRolesStore = new FileRolesStore(settings, env, resourceWatcherService, getLicenseState()); final NativeRolesStore nativeRolesStore = new NativeRolesStore(settings, client, getLicenseState(), securityIndex.get()); final ReservedRolesStore reservedRolesStore = new ReservedRolesStore(); @@ -473,13 +474,13 @@ Collection createComponents(Client client, ThreadPool threadPool, Cluste rolesProviders.addAll(extension.getRolesProviders(settings, resourceWatcherService)); } final CompositeRolesStore allRolesStore = new CompositeRolesStore(settings, fileRolesStore, nativeRolesStore, - reservedRolesStore, privilegeStore, rolesProviders, threadPool.getThreadContext(), getLicenseState()); + reservedRolesStore, privilegeStore, rolesProviders, threadPool.getThreadContext(), getLicenseState(), fieldPermissionsCache); securityIndex.get().addIndexStateListener(allRolesStore::onSecurityIndexStateChange); // to keep things simple, just invalidate all cached entries on license change. this happens so rarely that the impact should be // minimal getLicenseState().addListener(allRolesStore::invalidateAll); final AuthorizationService authzService = new AuthorizationService(settings, allRolesStore, clusterService, - auditTrailService, failureHandler, threadPool, anonymousUser, apiKeyService); + auditTrailService, failureHandler, threadPool, anonymousUser, apiKeyService, fieldPermissionsCache); components.add(nativeRolesStore); // used by roles actions components.add(reservedRolesStore); // used by roles actions components.add(allRolesStore); // for SecurityFeatureSet and clear roles cache diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java index 808817c4bc3eb..1c860766b4751 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/ApiKeyService.java @@ -38,7 +38,6 @@ import org.elasticsearch.xpack.core.security.authc.AuthenticationResult; import org.elasticsearch.xpack.core.security.authc.support.Hasher; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; -import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsCache; import org.elasticsearch.xpack.core.security.authz.permission.Role; import org.elasticsearch.xpack.core.security.user.User; import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore; @@ -96,8 +95,7 @@ public class ApiKeyService { private final Hasher hasher; private final boolean enabled; - public ApiKeyService(Settings settings, Clock clock, Client client, - SecurityIndexManager securityIndex, ClusterService clusterService) { + public ApiKeyService(Settings settings, Clock clock, Client client, SecurityIndexManager securityIndex, ClusterService clusterService) { this.clock = clock; this.client = client; this.securityIndex = securityIndex; @@ -221,25 +219,13 @@ void authenticateWithApiKeyIfPresent(ThreadContext ctx, ActionListener listener) { + public void getRoleForApiKey(Authentication authentication, CompositeRolesStore rolesStore, ActionListener listener) { if (authentication.getAuthenticationType() != Authentication.AuthenticationType.API_KEY) { throw new IllegalStateException("authentication type must be api key but is " + authentication.getAuthenticationType()); } final Map metadata = authentication.getMetadata(); final String apiKeyId = (String) metadata.get(API_KEY_ID_KEY); - final String contextKeyId = threadContext.getTransient(API_KEY_ID_KEY); - if (apiKeyId.equals(contextKeyId)) { - final Role preBuiltRole = threadContext.getTransient(API_KEY_ROLE_KEY); - if (preBuiltRole != null) { - listener.onResponse(preBuiltRole); - return; - } - } else if (contextKeyId != null) { - throw new IllegalStateException("authentication api key id [" + apiKeyId + "] does not match context value [" + - contextKeyId + "]"); - } final Map roleDescriptors = (Map) metadata.get(API_KEY_ROLE_DESCRIPTORS_KEY); final List roleDescriptorList = roleDescriptors.entrySet().stream() @@ -258,11 +244,7 @@ public void getRoleForApiKey(Authentication authentication, ThreadContext thread } }).collect(Collectors.toList()); - rolesStore.buildRoleFromDescriptors(roleDescriptorList, fieldPermissionsCache, ActionListener.wrap(role -> { - threadContext.putTransient(API_KEY_ID_KEY, apiKeyId); - threadContext.putTransient(API_KEY_ROLE_KEY, role); - listener.onResponse(role); - }, listener::onFailure)); + rolesStore.buildAndCacheRoleFromDescriptors(roleDescriptorList, apiKeyId, listener); } 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 ae5dd7e36cf59..99a8e9c7c48b3 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 @@ -112,7 +112,8 @@ public class AuthorizationService { public AuthorizationService(Settings settings, CompositeRolesStore rolesStore, ClusterService clusterService, AuditTrailService auditTrail, AuthenticationFailureHandler authcFailureHandler, - ThreadPool threadPool, AnonymousUser anonymousUser, ApiKeyService apiKeyService) { + ThreadPool threadPool, AnonymousUser anonymousUser, ApiKeyService apiKeyService, + FieldPermissionsCache fieldPermissionsCache) { this.rolesStore = rolesStore; this.clusterService = clusterService; this.auditTrail = auditTrail; @@ -122,7 +123,7 @@ public AuthorizationService(Settings settings, CompositeRolesStore rolesStore, C this.anonymousUser = anonymousUser; this.isAnonymousEnabled = AnonymousUser.isAnonymousEnabled(settings); this.anonymousAuthzExceptionEnabled = ANONYMOUS_AUTHORIZATION_EXCEPTION_SETTING.get(settings); - this.fieldPermissionsCache = new FieldPermissionsCache(settings); + this.fieldPermissionsCache = fieldPermissionsCache; this.apiKeyService = apiKeyService; } @@ -480,7 +481,7 @@ public void roles(User user, Authentication authentication, ActionListener final Authentication.AuthenticationType authType = authentication.getAuthenticationType(); if (authType == Authentication.AuthenticationType.API_KEY) { - apiKeyService.getRoleForApiKey(authentication, threadContext, rolesStore, fieldPermissionsCache, roleActionListener); + apiKeyService.getRoleForApiKey(authentication, rolesStore, roleActionListener); } else { Set roleNames = new HashSet<>(); Collections.addAll(roleNames, user.roles()); @@ -496,7 +497,7 @@ public void roles(User user, Authentication authentication, ActionListener } else if (roleNames.contains(ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR.getName())) { roleActionListener.onResponse(ReservedRolesStore.SUPERUSER_ROLE); } else { - rolesStore.roles(roleNames, fieldPermissionsCache, roleActionListener); + rolesStore.roles(roleNames, roleActionListener); } } } 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 b4c2a34fc747c..8b0f6e896176e 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 @@ -68,7 +68,7 @@ */ public class CompositeRolesStore { - + private static final String ROLES_STORE_SOURCE = "roles_stores"; private static final Setting CACHE_SIZE_SETTING = Setting.intSetting("xpack.security.authz.store.roles.cache.max_size", 10000, Property.NodeScope); private static final Setting NEGATIVE_LOOKUP_CACHE_SIZE_SETTING = @@ -92,7 +92,8 @@ public class CompositeRolesStore { private final NativeRolesStore nativeRolesStore; private final NativePrivilegeStore privilegeStore; private final XPackLicenseState licenseState; - private final Cache, Role> roleCache; + private final FieldPermissionsCache fieldPermissionsCache; + private final Cache roleCache; private final Cache negativeLookupCache; private final ThreadContext threadContext; private final AtomicLong numInvalidation = new AtomicLong(); @@ -102,13 +103,14 @@ public class CompositeRolesStore { public CompositeRolesStore(Settings settings, FileRolesStore fileRolesStore, NativeRolesStore nativeRolesStore, ReservedRolesStore reservedRolesStore, NativePrivilegeStore privilegeStore, List, ActionListener>> rolesProviders, - ThreadContext threadContext, XPackLicenseState licenseState) { + ThreadContext threadContext, XPackLicenseState licenseState, FieldPermissionsCache fieldPermissionsCache) { this.fileRolesStore = fileRolesStore; fileRolesStore.addListener(this::invalidate); this.nativeRolesStore = nativeRolesStore; this.privilegeStore = privilegeStore; this.licenseState = licenseState; - CacheBuilder, Role> builder = CacheBuilder.builder(); + this.fieldPermissionsCache = fieldPermissionsCache; + CacheBuilder builder = CacheBuilder.builder(); final int cacheSize = CACHE_SIZE_SETTING.get(settings); if (cacheSize >= 0) { builder.setMaximumWeight(cacheSize); @@ -133,8 +135,9 @@ public CompositeRolesStore(Settings settings, FileRolesStore fileRolesStore, Nat } } - public void roles(Set roleNames, FieldPermissionsCache fieldPermissionsCache, ActionListener roleActionListener) { - Role existing = roleCache.get(roleNames); + public void roles(Set roleNames, ActionListener roleActionListener) { + final RoleKey roleKey = new RoleKey(roleNames, ROLES_STORE_SOURCE); + Role existing = roleCache.get(roleKey); if (existing != null) { roleActionListener.onResponse(existing); } else { @@ -154,33 +157,53 @@ public void roles(Set roleNames, FieldPermissionsCache fieldPermissionsC .filter((rd) -> rd.isUsingDocumentOrFieldLevelSecurity() == false) .collect(Collectors.toSet()); } - logger.trace("Building role from descriptors [{}] for names [{}]", effectiveDescriptors, roleNames); - buildRoleFromDescriptors(effectiveDescriptors, fieldPermissionsCache, privilegeStore, ActionListener.wrap(role -> { - if (role != null && rolesRetrievalResult.isSuccess()) { - try (ReleasableLock ignored = readLock.acquire()) { - /* this is kinda spooky. We use a read/write lock to ensure we don't modify the cache if we hold - * the write lock (fetching stats for instance - which is kinda overkill?) but since we fetching - * stuff in an async fashion we need to make sure that if the cache got invalidated since we - * started the request we don't put a potential stale result in the cache, hence the - * numInvalidation.get() comparison to the number of invalidation when we started. we just try to - * be on the safe side and don't cache potentially stale results - */ - if (invalidationCounter == numInvalidation.get()) { - roleCache.computeIfAbsent(roleNames, (s) -> role); - } - } - - for (String missingRole : rolesRetrievalResult.getMissingRoles()) { - negativeLookupCache.computeIfAbsent(missingRole, s -> Boolean.TRUE); - } - } - roleActionListener.onResponse(role); - }, roleActionListener::onFailure)); + buildThenMaybeCacheRole(roleKey, effectiveDescriptors, rolesRetrievalResult.getMissingRoles(), + rolesRetrievalResult.isSuccess(), invalidationCounter, roleActionListener); }, roleActionListener::onFailure)); } } + public void buildAndCacheRoleFromDescriptors(Collection roleDescriptors, String source, + ActionListener listener) { + if (ROLES_STORE_SOURCE.equals(source)) { + throw new IllegalArgumentException("source [" + ROLES_STORE_SOURCE + "] is reserved for internal use"); + } + RoleKey roleKey = new RoleKey(roleDescriptors.stream().map(RoleDescriptor::getName).collect(Collectors.toSet()), source); + Role existing = roleCache.get(roleKey); + if (existing != null) { + listener.onResponse(existing); + } else { + final long invalidationCounter = numInvalidation.get(); + buildThenMaybeCacheRole(roleKey, roleDescriptors, Collections.emptySet(), true, invalidationCounter, listener); + } + } + + private void buildThenMaybeCacheRole(RoleKey roleKey, Collection roleDescriptors, Set missing, + boolean tryCache, long invalidationCounter, ActionListener listener) { + logger.trace("Building role from descriptors [{}] for names [{}] from source [{}]", roleDescriptors, roleKey.names, roleKey.source); + buildRoleFromDescriptors(roleDescriptors, fieldPermissionsCache, privilegeStore, ActionListener.wrap(role -> { + if (role != null && tryCache) { + try (ReleasableLock ignored = readLock.acquire()) { + /* this is kinda spooky. We use a read/write lock to ensure we don't modify the cache if we hold + * the write lock (fetching stats for instance - which is kinda overkill?) but since we fetching + * stuff in an async fashion we need to make sure that if the cache got invalidated since we + * started the request we don't put a potential stale result in the cache, hence the + * numInvalidation.get() comparison to the number of invalidation when we started. we just try to + * be on the safe side and don't cache potentially stale results + */ + if (invalidationCounter == numInvalidation.get()) { + roleCache.computeIfAbsent(roleKey, (s) -> role); + } + } + + for (String missingRole : missing) { + negativeLookupCache.computeIfAbsent(missingRole, s -> Boolean.TRUE); + } + } + listener.onResponse(role); + }, listener::onFailure)); + } public void getRoleDescriptors(Set roleNames, ActionListener> listener) { roleDescriptors(roleNames, ActionListener.wrap(rolesRetrievalResult -> { if (rolesRetrievalResult.isSuccess()) { @@ -241,11 +264,6 @@ private String names(Collection descriptors) { return descriptors.stream().map(RoleDescriptor::getName).collect(Collectors.joining(",")); } - public void buildRoleFromDescriptors(Collection roleDescriptors, FieldPermissionsCache fieldPermissionsCache, - ActionListener listener) { - buildRoleFromDescriptors(roleDescriptors, fieldPermissionsCache, privilegeStore, listener); - } - public static void buildRoleFromDescriptors(Collection roleDescriptors, FieldPermissionsCache fieldPermissionsCache, NativePrivilegeStore privilegeStore, ActionListener listener) { if (roleDescriptors.isEmpty()) { @@ -346,10 +364,10 @@ public void invalidate(String role) { // the cache cannot be modified while doing this operation per the terms of the cache iterator try (ReleasableLock ignored = writeLock.acquire()) { - Iterator> keyIter = roleCache.keys().iterator(); + Iterator keyIter = roleCache.keys().iterator(); while (keyIter.hasNext()) { - Set key = keyIter.next(); - if (key.contains(role)) { + RoleKey key = keyIter.next(); + if (key.names.contains(role)) { keyIter.remove(); } } @@ -362,10 +380,10 @@ public void invalidate(Set roles) { // the cache cannot be modified while doing this operation per the terms of the cache iterator try (ReleasableLock ignored = writeLock.acquire()) { - Iterator> keyIter = roleCache.keys().iterator(); + Iterator keyIter = roleCache.keys().iterator(); while (keyIter.hasNext()) { - Set key = keyIter.next(); - if (Sets.haveEmptyIntersection(key, roles) == false) { + RoleKey key = keyIter.next(); + if (Sets.haveEmptyIntersection(key.names, roles) == false) { keyIter.remove(); } } @@ -461,6 +479,31 @@ private Set getMissingRoles() { } } + private static final class RoleKey { + + private final Set names; + private final String source; + + private RoleKey(Set names, String source) { + this.names = Objects.requireNonNull(names); + this.source = Objects.requireNonNull(source); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RoleKey roleKey = (RoleKey) o; + return names.equals(roleKey.names) && + source.equals(roleKey.source); + } + + @Override + public int hashCode() { + return Objects.hash(names, source); + } + } + public static List> getSettings() { return Arrays.asList(CACHE_SIZE_SETTING, NEGATIVE_LOOKUP_CACHE_SIZE_SETTING); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyServiceTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyServiceTests.java index 4f75f1158a968..cee5711780f11 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyServiceTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/ApiKeyServiceTests.java @@ -28,7 +28,6 @@ import org.elasticsearch.xpack.core.security.authc.AuthenticationResult; import org.elasticsearch.xpack.core.security.authc.support.Hasher; import org.elasticsearch.xpack.core.security.authz.RoleDescriptor; -import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsCache; import org.elasticsearch.xpack.core.security.authz.permission.Role; import org.elasticsearch.xpack.core.security.authz.store.ReservedRolesStore; import org.elasticsearch.xpack.core.security.user.User; @@ -45,7 +44,6 @@ import java.util.HashMap; import java.util.Map; -import static org.hamcrest.Matchers.containsString; import static org.mockito.Matchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; @@ -151,38 +149,6 @@ public void testValidateApiKey() throws Exception { assertFalse(result.isAuthenticated()); } - public void testGetRolesForApiKeyFromContext() throws Exception { - final String keyId = randomAlphaOfLength(12); - final Authentication authentication = new Authentication(new User("joe"), new RealmRef("apikey", "apikey", "node"), null, - Version.CURRENT, AuthenticationType.API_KEY, Collections.singletonMap(ApiKeyService.API_KEY_ID_KEY, keyId)); - final ThreadContext threadContext = new ThreadContext(Settings.EMPTY); - threadContext.putTransient(ApiKeyService.API_KEY_ID_KEY, keyId); - threadContext.putTransient(ApiKeyService.API_KEY_ROLE_KEY, ReservedRolesStore.SUPERUSER_ROLE); - ApiKeyService service = new ApiKeyService(Settings.EMPTY, Clock.systemUTC(), null, null, - ClusterServiceUtils.createClusterService(threadPool)); - PlainActionFuture roleFuture = new PlainActionFuture<>(); - service.getRoleForApiKey(authentication, threadContext, mock(CompositeRolesStore.class), new FieldPermissionsCache(Settings.EMPTY), - roleFuture); - Role role = roleFuture.get(); - assertEquals(ReservedRolesStore.SUPERUSER_ROLE, role); - } - - public void testGetRolesForApiKeyIncorrectKeyId() { - final String keyId = randomAlphaOfLength(12); - final Authentication authentication = new Authentication(new User("joe"), new RealmRef("apikey", "apikey", "node"), null, - Version.CURRENT, AuthenticationType.API_KEY, Collections.singletonMap(ApiKeyService.API_KEY_ID_KEY, keyId)); - final ThreadContext threadContext = new ThreadContext(Settings.EMPTY); - threadContext.putTransient(ApiKeyService.API_KEY_ID_KEY, randomAlphaOfLength(11)); - threadContext.putTransient(ApiKeyService.API_KEY_ROLE_KEY, ReservedRolesStore.SUPERUSER_ROLE); - ApiKeyService service = new ApiKeyService(Settings.EMPTY, Clock.systemUTC(), null, null, - ClusterServiceUtils.createClusterService(threadPool)); - PlainActionFuture roleFuture = new PlainActionFuture<>(); - IllegalStateException ise = expectThrows(IllegalStateException.class, () -> service.getRoleForApiKey(authentication, threadContext, - mock(CompositeRolesStore.class),new FieldPermissionsCache(Settings.EMPTY), roleFuture)); - assertThat(ise.getMessage(), containsString(keyId)); - assertThat(ise.getMessage(), containsString(threadContext.getTransient(ApiKeyService.API_KEY_ID_KEY))); - } - public void testGetRolesForApiKeyNotInContext() throws Exception { Map superUserRdMap; try (XContentBuilder builder = JsonXContent.contentBuilder()) { @@ -214,10 +180,10 @@ public void testGetRolesForApiKeyNotInContext() throws Exception { listener.onFailure(new IllegalStateException("unexpected role name " + descriptors.iterator().next().getName())); } return Void.TYPE; - }).when(rolesStore).buildRoleFromDescriptors(any(Collection.class), any(FieldPermissionsCache.class), any(ActionListener.class)); + }).when(rolesStore).buildAndCacheRoleFromDescriptors(any(Collection.class), any(String.class), any(ActionListener.class)); PlainActionFuture roleFuture = new PlainActionFuture<>(); - service.getRoleForApiKey(authentication, threadContext, rolesStore, new FieldPermissionsCache(Settings.EMPTY), roleFuture); + service.getRoleForApiKey(authentication, rolesStore, roleFuture); Role role = roleFuture.get(); assertThat(role.names(), arrayContaining("superuser")); } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java index 797a7694fb607..fa1f7274cf8db 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/AuthorizationServiceTests.java @@ -211,7 +211,7 @@ public void setup() { ).when(privilegesStore).getPrivileges(any(Collection.class), any(Collection.class), any(ActionListener.class)); doAnswer((i) -> { - ActionListener callback = (ActionListener) i.getArguments()[2]; + ActionListener callback = (ActionListener) i.getArguments()[1]; Set names = (Set) i.getArguments()[0]; assertNotNull(names); Set roleDescriptors = new HashSet<>(); @@ -230,11 +230,11 @@ public void setup() { ); } return Void.TYPE; - }).when(rolesStore).roles(any(Set.class), any(FieldPermissionsCache.class), any(ActionListener.class)); + }).when(rolesStore).roles(any(Set.class), any(ActionListener.class)); apiKeyService = mock(ApiKeyService.class); authorizationService = new AuthorizationService(settings, rolesStore, clusterService, auditTrail, new DefaultAuthenticationFailureHandler(Collections.emptyMap()), threadPool, new AnonymousUser(settings), - apiKeyService); + apiKeyService, new FieldPermissionsCache(Settings.EMPTY)); } private void authorize(Authentication authentication, String action, TransportRequest request) { @@ -628,7 +628,8 @@ public void testDenialForAnonymousUser() throws IOException { Settings settings = Settings.builder().put(AnonymousUser.ROLES_SETTING.getKey(), "a_all").build(); final AnonymousUser anonymousUser = new AnonymousUser(settings); authorizationService = new AuthorizationService(settings, rolesStore, clusterService, auditTrail, - new DefaultAuthenticationFailureHandler(Collections.emptyMap()), threadPool, anonymousUser, apiKeyService); + new DefaultAuthenticationFailureHandler(Collections.emptyMap()), threadPool, anonymousUser, apiKeyService, + new FieldPermissionsCache(settings)); RoleDescriptor role = new RoleDescriptor("a_all", null, new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() }, null); @@ -655,7 +656,7 @@ public void testDenialForAnonymousUserAuthorizationExceptionDisabled() throws IO final Authentication authentication = createAuthentication(new AnonymousUser(settings)); authorizationService = new AuthorizationService(settings, rolesStore, clusterService, auditTrail, new DefaultAuthenticationFailureHandler(Collections.emptyMap()), threadPool, new AnonymousUser(settings), - apiKeyService); + apiKeyService, new FieldPermissionsCache(settings)); RoleDescriptor role = new RoleDescriptor("a_all", null, new IndicesPrivileges[]{IndicesPrivileges.builder().indices("a").privileges("all").build()}, null); @@ -968,7 +969,8 @@ public void testAnonymousRolesAreAppliedToOtherUsers() throws IOException { 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(Collections.emptyMap()), threadPool, anonymousUser, apiKeyService); + new DefaultAuthenticationFailureHandler(Collections.emptyMap()), threadPool, anonymousUser, apiKeyService, + new FieldPermissionsCache(settings)); roleMap.put("anonymous_user_role", new RoleDescriptor("anonymous_user_role", new String[]{"all"}, new IndicesPrivileges[]{IndicesPrivileges.builder().indices("a").privileges("all").build()}, null)); mockEmptyMetaData(); @@ -1004,7 +1006,8 @@ public void testAnonymousUserEnabledRoleAdded() throws IOException { 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(Collections.emptyMap()), threadPool, anonymousUser, apiKeyService); + new DefaultAuthenticationFailureHandler(Collections.emptyMap()), threadPool, anonymousUser, apiKeyService, + new FieldPermissionsCache(settings)); roleMap.put("anonymous_user_role", new RoleDescriptor("anonymous_user_role", new String[]{"all"}, new IndicesPrivileges[]{IndicesPrivileges.builder().indices("a").privileges("all").build()}, null)); mockEmptyMetaData(); @@ -1475,15 +1478,13 @@ public void testApiKeyAuthUsesApiKeyService() throws IOException { AuditUtil.getOrGenerateRequestId(threadContext); final Authentication authentication = createAuthentication(new User("test api key user", "api_key"), AuthenticationType.API_KEY); doAnswer(invocationOnMock -> { - ActionListener listener = (ActionListener) invocationOnMock.getArguments()[4]; + ActionListener listener = (ActionListener) invocationOnMock.getArguments()[2]; listener.onResponse(ReservedRolesStore.SUPERUSER_ROLE); return Void.TYPE; - }).when(apiKeyService).getRoleForApiKey(eq(authentication), eq(threadContext), eq(rolesStore), any(FieldPermissionsCache.class), - any(ActionListener.class)); + }).when(apiKeyService).getRoleForApiKey(eq(authentication), eq(rolesStore), any(ActionListener.class)); authorize(authentication, "cluster:admin/foo", new ClearScrollRequest()); - verify(apiKeyService).getRoleForApiKey(eq(authentication), eq(threadContext), eq(rolesStore), any(FieldPermissionsCache.class), - any(ActionListener.class)); + verify(apiKeyService).getRoleForApiKey(eq(authentication), eq(rolesStore), any(ActionListener.class)); verifyZeroInteractions(rolesStore); } } diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java index 89ee0be915984..6582fc8782fc8 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java @@ -188,13 +188,13 @@ public void setup() { ); } return Void.TYPE; - }).when(rolesStore).roles(any(Set.class), any(FieldPermissionsCache.class), any(ActionListener.class)); + }).when(rolesStore).roles(any(Set.class), any(ActionListener.class)); ClusterService clusterService = mock(ClusterService.class); when(clusterService.getClusterSettings()).thenReturn(new ClusterSettings(settings, ClusterSettings.BUILT_IN_CLUSTER_SETTINGS)); authzService = new AuthorizationService(settings, rolesStore, clusterService, mock(AuditTrailService.class), new DefaultAuthenticationFailureHandler(Collections.emptyMap()), mock(ThreadPool.class), - new AnonymousUser(settings), mock(ApiKeyService.class)); + new AnonymousUser(settings), mock(ApiKeyService.class), new FieldPermissionsCache(settings)); defaultIndicesResolver = new IndicesAndAliasesResolver(settings, clusterService); } 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 6801ffd6bdf84..866b98096f461 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 @@ -79,6 +79,8 @@ public class CompositeRolesStoreTests extends ESTestCase { .put(XPackSettings.SECURITY_ENABLED.getKey(), true) .build(); + private final FieldPermissionsCache cache = new FieldPermissionsCache(Settings.EMPTY); + public void testRolesWhenDlsFlsUnlicensed() throws IOException { XPackLicenseState licenseState = mock(XPackLicenseState.class); when(licenseState.isDocumentAndFieldLevelSecurityAllowed()).thenReturn(false); @@ -126,23 +128,22 @@ public void testRolesWhenDlsFlsUnlicensed() throws IOException { when(fileRolesStore.roleDescriptors(Collections.singleton("no_fls_dls"))).thenReturn(Collections.singleton(noFlsDlsRole)); CompositeRolesStore compositeRolesStore = new CompositeRolesStore(Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Collections.emptyList(), - new ThreadContext(Settings.EMPTY), licenseState); + new ThreadContext(Settings.EMPTY), licenseState, cache); - FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); PlainActionFuture roleFuture = new PlainActionFuture<>(); - compositeRolesStore.roles(Collections.singleton("fls"), fieldPermissionsCache, roleFuture); + compositeRolesStore.roles(Collections.singleton("fls"), roleFuture); assertEquals(Role.EMPTY, roleFuture.actionGet()); roleFuture = new PlainActionFuture<>(); - compositeRolesStore.roles(Collections.singleton("dls"), fieldPermissionsCache, roleFuture); + compositeRolesStore.roles(Collections.singleton("dls"), roleFuture); assertEquals(Role.EMPTY, roleFuture.actionGet()); roleFuture = new PlainActionFuture<>(); - compositeRolesStore.roles(Collections.singleton("fls_dls"), fieldPermissionsCache, roleFuture); + compositeRolesStore.roles(Collections.singleton("fls_dls"), roleFuture); assertEquals(Role.EMPTY, roleFuture.actionGet()); roleFuture = new PlainActionFuture<>(); - compositeRolesStore.roles(Collections.singleton("no_fls_dls"), fieldPermissionsCache, roleFuture); + compositeRolesStore.roles(Collections.singleton("no_fls_dls"), roleFuture); assertNotEquals(Role.EMPTY, roleFuture.actionGet()); } @@ -192,23 +193,22 @@ public void testRolesWhenDlsFlsLicensed() throws IOException { when(fileRolesStore.roleDescriptors(Collections.singleton("no_fls_dls"))).thenReturn(Collections.singleton(noFlsDlsRole)); CompositeRolesStore compositeRolesStore = new CompositeRolesStore(Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Collections.emptyList(), - new ThreadContext(Settings.EMPTY), licenseState); + new ThreadContext(Settings.EMPTY), licenseState, cache); - FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); PlainActionFuture roleFuture = new PlainActionFuture<>(); - compositeRolesStore.roles(Collections.singleton("fls"), fieldPermissionsCache, roleFuture); + compositeRolesStore.roles(Collections.singleton("fls"), roleFuture); assertNotEquals(Role.EMPTY, roleFuture.actionGet()); roleFuture = new PlainActionFuture<>(); - compositeRolesStore.roles(Collections.singleton("dls"), fieldPermissionsCache, roleFuture); + compositeRolesStore.roles(Collections.singleton("dls"), roleFuture); assertNotEquals(Role.EMPTY, roleFuture.actionGet()); roleFuture = new PlainActionFuture<>(); - compositeRolesStore.roles(Collections.singleton("fls_dls"), fieldPermissionsCache, roleFuture); + compositeRolesStore.roles(Collections.singleton("fls_dls"), roleFuture); assertNotEquals(Role.EMPTY, roleFuture.actionGet()); roleFuture = new PlainActionFuture<>(); - compositeRolesStore.roles(Collections.singleton("no_fls_dls"), fieldPermissionsCache, roleFuture); + compositeRolesStore.roles(Collections.singleton("no_fls_dls"), roleFuture); assertNotEquals(Role.EMPTY, roleFuture.actionGet()); } @@ -228,13 +228,12 @@ public void testNegativeLookupsAreCached() { final CompositeRolesStore compositeRolesStore = new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS), - new XPackLicenseState(SECURITY_ENABLED_SETTINGS)); + new XPackLicenseState(SECURITY_ENABLED_SETTINGS), cache); verify(fileRolesStore).addListener(any(Consumer.class)); // adds a listener in ctor final String roleName = randomAlphaOfLengthBetween(1, 10); PlainActionFuture future = new PlainActionFuture<>(); - final FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); - compositeRolesStore.roles(Collections.singleton(roleName), fieldPermissionsCache, future); + compositeRolesStore.roles(Collections.singleton(roleName), future); final Role role = future.actionGet(); assertEquals(Role.EMPTY, role); verify(reservedRolesStore).accept(anySetOf(String.class), any(ActionListener.class)); @@ -250,7 +249,7 @@ public void testNegativeLookupsAreCached() { : Collections.singleton(roleName); for (int i = 0; i < numberOfTimesToCall; i++) { future = new PlainActionFuture<>(); - compositeRolesStore.roles(names, fieldPermissionsCache, future); + compositeRolesStore.roles(names, future); future.actionGet(); } @@ -279,13 +278,12 @@ public void testNegativeLookupsCacheDisabled() { .build(); final CompositeRolesStore compositeRolesStore = new CompositeRolesStore(settings, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(settings), - new XPackLicenseState(settings)); + new XPackLicenseState(settings), cache); verify(fileRolesStore).addListener(any(Consumer.class)); // adds a listener in ctor final String roleName = randomAlphaOfLengthBetween(1, 10); PlainActionFuture future = new PlainActionFuture<>(); - final FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); - compositeRolesStore.roles(Collections.singleton(roleName), fieldPermissionsCache, future); + compositeRolesStore.roles(Collections.singleton(roleName), future); final Role role = future.actionGet(); assertEquals(Role.EMPTY, role); verify(reservedRolesStore).accept(anySetOf(String.class), any(ActionListener.class)); @@ -314,13 +312,12 @@ public void testNegativeLookupsAreNotCachedWithFailures() { final CompositeRolesStore compositeRolesStore = new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS), - new XPackLicenseState(SECURITY_ENABLED_SETTINGS)); + new XPackLicenseState(SECURITY_ENABLED_SETTINGS), cache); verify(fileRolesStore).addListener(any(Consumer.class)); // adds a listener in ctor final String roleName = randomAlphaOfLengthBetween(1, 10); PlainActionFuture future = new PlainActionFuture<>(); - final FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); - compositeRolesStore.roles(Collections.singleton(roleName), fieldPermissionsCache, future); + compositeRolesStore.roles(Collections.singleton(roleName), future); final Role role = future.actionGet(); assertEquals(Role.EMPTY, role); verify(reservedRolesStore).accept(anySetOf(String.class), any(ActionListener.class)); @@ -333,7 +330,7 @@ public void testNegativeLookupsAreNotCachedWithFailures() { final Set names = Collections.singleton(roleName); for (int i = 0; i < numberOfTimesToCall; i++) { future = new PlainActionFuture<>(); - compositeRolesStore.roles(names, fieldPermissionsCache, future); + compositeRolesStore.roles(names, future); future.actionGet(); } @@ -392,12 +389,11 @@ public void testCustomRolesProviders() { final CompositeRolesStore compositeRolesStore = new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Arrays.asList(inMemoryProvider1, inMemoryProvider2), - new ThreadContext(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS)); + new ThreadContext(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS), cache); final Set roleNames = Sets.newHashSet("roleA", "roleB", "unknown"); PlainActionFuture future = new PlainActionFuture<>(); - final FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); - compositeRolesStore.roles(roleNames, fieldPermissionsCache, future); + compositeRolesStore.roles(roleNames, future); final Role role = future.actionGet(); // make sure custom roles providers populate roles correctly @@ -414,7 +410,7 @@ public void testCustomRolesProviders() { final int numberOfTimesToCall = scaledRandomIntBetween(1, 8); for (int i = 0; i < numberOfTimesToCall; i++) { future = new PlainActionFuture<>(); - compositeRolesStore.roles(Collections.singleton("unknown"), fieldPermissionsCache, future); + compositeRolesStore.roles(Collections.singleton("unknown"), future); future.actionGet(); } @@ -597,12 +593,11 @@ public void testCustomRolesProviderFailures() throws Exception { final CompositeRolesStore compositeRolesStore = new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Arrays.asList(inMemoryProvider1, failingProvider), - new ThreadContext(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS)); + new ThreadContext(SECURITY_ENABLED_SETTINGS), new XPackLicenseState(SECURITY_ENABLED_SETTINGS), cache); final Set roleNames = Sets.newHashSet("roleA", "roleB", "unknown"); PlainActionFuture future = new PlainActionFuture<>(); - final FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); - compositeRolesStore.roles(roleNames, fieldPermissionsCache, future); + compositeRolesStore.roles(roleNames, future); try { future.get(); fail("provider should have thrown a failure"); @@ -640,12 +635,11 @@ public void testCustomRolesProvidersLicensing() { xPackLicenseState.update(randomFrom(OperationMode.BASIC, OperationMode.GOLD, OperationMode.STANDARD), true, null); CompositeRolesStore compositeRolesStore = new CompositeRolesStore( Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), - Arrays.asList(inMemoryProvider), new ThreadContext(Settings.EMPTY), xPackLicenseState); + Arrays.asList(inMemoryProvider), new ThreadContext(Settings.EMPTY), xPackLicenseState, cache); Set roleNames = Sets.newHashSet("roleA"); PlainActionFuture future = new PlainActionFuture<>(); - FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); - compositeRolesStore.roles(roleNames, fieldPermissionsCache, future); + compositeRolesStore.roles(roleNames, future); Role role = future.actionGet(); // no roles should've been populated, as the license doesn't permit custom role providers @@ -653,13 +647,12 @@ Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(Nativ compositeRolesStore = new CompositeRolesStore( Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), - Arrays.asList(inMemoryProvider), new ThreadContext(Settings.EMPTY), xPackLicenseState); + Arrays.asList(inMemoryProvider), new ThreadContext(Settings.EMPTY), xPackLicenseState, cache); // these licenses allow custom role providers xPackLicenseState.update(randomFrom(OperationMode.PLATINUM, OperationMode.TRIAL), true, null); roleNames = Sets.newHashSet("roleA"); future = new PlainActionFuture<>(); - fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); - compositeRolesStore.roles(roleNames, fieldPermissionsCache, future); + compositeRolesStore.roles(roleNames, future); role = future.actionGet(); // roleA should've been populated by the custom role provider, because the license allows it @@ -668,12 +661,11 @@ Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(Nativ // license expired, don't allow custom role providers compositeRolesStore = new CompositeRolesStore( Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), - Arrays.asList(inMemoryProvider), new ThreadContext(Settings.EMPTY), xPackLicenseState); + Arrays.asList(inMemoryProvider), new ThreadContext(Settings.EMPTY), xPackLicenseState, cache); xPackLicenseState.update(randomFrom(OperationMode.PLATINUM, OperationMode.TRIAL), false, null); roleNames = Sets.newHashSet("roleA"); future = new PlainActionFuture<>(); - fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); - compositeRolesStore.roles(roleNames, fieldPermissionsCache, future); + compositeRolesStore.roles(roleNames, future); role = future.actionGet(); assertEquals(0, role.indices().groups().length); } @@ -694,7 +686,7 @@ public void testCacheClearOnIndexHealthChange() { CompositeRolesStore compositeRolesStore = new CompositeRolesStore( Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(Settings.EMPTY), - new XPackLicenseState(SECURITY_ENABLED_SETTINGS)) { + new XPackLicenseState(SECURITY_ENABLED_SETTINGS), cache) { @Override public void invalidateAll() { numInvalidation.incrementAndGet(); @@ -746,7 +738,7 @@ public void testCacheClearOnIndexOutOfDateChange() { CompositeRolesStore compositeRolesStore = new CompositeRolesStore(SECURITY_ENABLED_SETTINGS, fileRolesStore, nativeRolesStore, reservedRolesStore, mock(NativePrivilegeStore.class), Collections.emptyList(), new ThreadContext(SECURITY_ENABLED_SETTINGS), - new XPackLicenseState(SECURITY_ENABLED_SETTINGS)) { + new XPackLicenseState(SECURITY_ENABLED_SETTINGS), cache) { @Override public void invalidateAll() { numInvalidation.incrementAndGet(); From 90197af830fb43e95c6e4e047266970f2539ea30 Mon Sep 17 00:00:00 2001 From: jaymode Date: Fri, 7 Dec 2018 13:17:25 -0700 Subject: [PATCH 2/3] fix bad mock in test --- .../xpack/security/authz/IndicesAndAliasesResolverTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java index 6582fc8782fc8..b7a85407a6f36 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authz/IndicesAndAliasesResolverTests.java @@ -169,7 +169,7 @@ public void setup() { final FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY); doAnswer((i) -> { ActionListener callback = - (ActionListener) i.getArguments()[2]; + (ActionListener) i.getArguments()[1]; Set names = (Set) i.getArguments()[0]; assertNotNull(names); Set roleDescriptors = new HashSet<>(); From ada3e19f4bf35d91b41fc3bc07132377c65883b6 Mon Sep 17 00:00:00 2001 From: jaymode Date: Mon, 10 Dec 2018 08:17:44 -0700 Subject: [PATCH 3/3] add missing newline --- .../xpack/security/authz/store/CompositeRolesStore.java | 1 + 1 file changed, 1 insertion(+) 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 8b0f6e896176e..378760604811d 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 @@ -204,6 +204,7 @@ private void buildThenMaybeCacheRole(RoleKey roleKey, Collection listener.onResponse(role); }, listener::onFailure)); } + public void getRoleDescriptors(Set roleNames, ActionListener> listener) { roleDescriptors(roleNames, ActionListener.wrap(rolesRetrievalResult -> { if (rolesRetrievalResult.isSuccess()) {