Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Client and RoleMapping in custom Realms #50534

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@

import org.apache.lucene.util.SPIClassIterator;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.core.security.authc.AuthenticationFailureHandler;
import org.elasticsearch.xpack.core.security.authc.Realm;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.store.RoleRetrievalResult;
Expand All @@ -28,16 +32,32 @@
*/
public interface SecurityExtension {

/**
* This interface provides access to components (clients and services) that may be used
* within custom realms and role providers.
*/
interface SecurityComponents {
/** An internal client for retrieving information/data from this cluster */
Client client();
/** The Elasticsearch thread pools */
ThreadPool threadPool();
albertzaharovits marked this conversation as resolved.
Show resolved Hide resolved
/** Provides the ability to monitor files for changes */
ResourceWatcherService resourceWatcherService();
/** Access to listen to changes in cluster state and settings */
ClusterService clusterService();
/** Provides support for mapping users' roles from groups and metadata */
UserRoleMapper roleMapper();
}
/**
* Returns authentication realm implementations added by this extension.
*
* The key of the returned {@link Map} is the type name of the realm, and the value
* is a {@link Realm.Factory} which will construct
* that realm for use in authentication when that realm type is configured.
*
* @param resourceWatcherService Use to watch configuration files for changes
* @param components Access to components that may be used to build realms
*/
default Map<String, Realm.Factory> getRealms(ResourceWatcherService resourceWatcherService) {
default Map<String, Realm.Factory> getRealms(SecurityComponents components) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think a Settings object might be useful as well? (same for other methods)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Settings is on RealmConfig, so I didn't add it here, but I think that was a mistake.
It makes sense to have everything available in 1 place. I'll add environment and settings to the components interface.

return Collections.emptyMap();
}

Expand All @@ -46,8 +66,10 @@ default Map<String, Realm.Factory> getRealms(ResourceWatcherService resourceWatc
*
* Only one installed extension may have an authentication failure handler. If more than
* one extension returns a non-null handler, an error is raised.
*
* @param components Access to components that may be used to build the handler
*/
default AuthenticationFailureHandler getAuthenticationFailureHandler() {
default AuthenticationFailureHandler getAuthenticationFailureHandler(SecurityComponents components) {
return null;
}
albertzaharovits marked this conversation as resolved.
Show resolved Hide resolved

Expand All @@ -73,10 +95,10 @@ default AuthenticationFailureHandler getAuthenticationFailureHandler() {
* By default, an empty list is returned.
*
* @param settings The configured settings for the node
* @param resourceWatcherService Use to watch configuration files for changes
* @param components Access to components that may be used to build roles
*/
default List<BiConsumer<Set<String>, ActionListener<RoleRetrievalResult>>>
getRolesProviders(Settings settings, ResourceWatcherService resourceWatcherService) {
getRolesProviders(Settings settings, SecurityComponents components) {
return Collections.emptyList();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authc.support;
package org.elasticsearch.xpack.core.security.authc.support;

import org.elasticsearch.xpack.core.security.authc.Realm;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authc.support;
package org.elasticsearch.xpack.core.security.authc.support;

import com.unboundid.ldap.sdk.DN;
import com.unboundid.ldap.sdk.LDAPException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@
import org.elasticsearch.xpack.security.rest.action.user.RestHasPrivilegesAction;
import org.elasticsearch.xpack.security.rest.action.user.RestPutUserAction;
import org.elasticsearch.xpack.security.rest.action.user.RestSetEnabledAction;
import org.elasticsearch.xpack.security.support.ExtensionComponents;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.elasticsearch.xpack.security.support.SecurityStatusChangeListener;
import org.elasticsearch.xpack.security.transport.SecurityHttpSettings;
Expand Down Expand Up @@ -391,10 +392,12 @@ Collection<Object> createComponents(Client client, ThreadPool threadPool, Cluste
final AnonymousUser anonymousUser = new AnonymousUser(settings);
final ReservedRealm reservedRealm = new ReservedRealm(env, settings, nativeUsersStore,
anonymousUser, securityIndex.get(), threadPool);
final SecurityExtension.SecurityComponents extensionComponents = new ExtensionComponents(client, threadPool, clusterService,
resourceWatcherService, nativeRoleMappingStore);
Map<String, Realm.Factory> realmFactories = new HashMap<>(InternalRealms.getFactories(threadPool, resourceWatcherService,
getSslService(), nativeUsersStore, nativeRoleMappingStore, securityIndex.get()));
for (SecurityExtension extension : securityExtensions) {
Map<String, Realm.Factory> newRealms = extension.getRealms(resourceWatcherService);
Map<String, Realm.Factory> newRealms = extension.getRealms(extensionComponents);
for (Map.Entry<String, Realm.Factory> entry : newRealms.entrySet()) {
if (realmFactories.put(entry.getKey(), entry.getValue()) != null) {
throw new IllegalArgumentException("Realm type [" + entry.getKey() + "] is already registered");
Expand All @@ -420,7 +423,7 @@ Collection<Object> createComponents(Client client, ThreadPool threadPool, Cluste
final ReservedRolesStore reservedRolesStore = new ReservedRolesStore();
List<BiConsumer<Set<String>, ActionListener<RoleRetrievalResult>>> rolesProviders = new ArrayList<>();
for (SecurityExtension extension : securityExtensions) {
rolesProviders.addAll(extension.getRolesProviders(settings, resourceWatcherService));
rolesProviders.addAll(extension.getRolesProviders(settings, extensionComponents));
}

final ApiKeyService apiKeyService = new ApiKeyService(settings, Clock.systemUTC(), client, getLicenseState(), securityIndex.get(),
Expand All @@ -436,7 +439,7 @@ Collection<Object> createComponents(Client client, ThreadPool threadPool, Cluste
getLicenseState().addListener(allRolesStore::invalidateAll);
getLicenseState().addListener(new SecurityStatusChangeListener(getLicenseState()));

final AuthenticationFailureHandler failureHandler = createAuthenticationFailureHandler(realms);
final AuthenticationFailureHandler failureHandler = createAuthenticationFailureHandler(realms, extensionComponents);
authcService.set(new AuthenticationService(settings, realms, auditTrailService, failureHandler, threadPool,
anonymousUser, tokenService, apiKeyService));
components.add(authcService.get());
Expand Down Expand Up @@ -496,11 +499,12 @@ private AuthorizationEngine getAuthorizationEngine() {
return authorizationEngine;
}

private AuthenticationFailureHandler createAuthenticationFailureHandler(final Realms realms) {
private AuthenticationFailureHandler createAuthenticationFailureHandler(final Realms realms,
final SecurityExtension.SecurityComponents components) {
AuthenticationFailureHandler failureHandler = null;
String extensionName = null;
for (SecurityExtension extension : securityExtensions) {
AuthenticationFailureHandler extensionFailureHandler = extension.getAuthenticationFailureHandler();
AuthenticationFailureHandler extensionFailureHandler = extension.getAuthenticationFailureHandler(components);
if (extensionFailureHandler != null && failureHandler != null) {
throw new IllegalStateException("Extensions [" + extensionName + "] and [" + extension.toString() + "] "
+ "both set an authentication failure handler");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import org.elasticsearch.xpack.core.security.authc.Realm;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.authc.Realms;
import org.elasticsearch.xpack.security.authc.support.CachingRealm;
import org.elasticsearch.xpack.core.security.authc.support.CachingRealm;

import java.io.IOException;
import java.util.List;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
import org.elasticsearch.xpack.core.security.authc.kerberos.KerberosRealmSettings;
import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.security.authc.support.CachingRealm;
import org.elasticsearch.xpack.core.security.authc.support.CachingRealm;
import org.elasticsearch.xpack.security.authc.support.DelegatedAuthorizationSupport;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore;
import org.ietf.jgss.GSSException;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
import org.elasticsearch.xpack.security.authc.ldap.support.SessionFactory;
import org.elasticsearch.xpack.security.authc.support.CachingUsernamePasswordRealm;
import org.elasticsearch.xpack.security.authc.support.DelegatedAuthorizationSupport;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper.UserData;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper.UserData;
import org.elasticsearch.xpack.security.authc.support.mapper.CompositeRoleMapper;
import org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
import org.elasticsearch.xpack.core.ssl.SSLService;
import org.elasticsearch.xpack.security.authc.TokenService;
import org.elasticsearch.xpack.security.authc.support.DelegatedAuthorizationSupport;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper;

import java.net.URI;
import java.net.URISyntaxException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@
import org.elasticsearch.xpack.core.ssl.SSLConfigurationSettings;
import org.elasticsearch.xpack.security.authc.BytesKey;
import org.elasticsearch.xpack.security.authc.TokenService;
import org.elasticsearch.xpack.security.authc.support.CachingRealm;
import org.elasticsearch.xpack.core.security.authc.support.CachingRealm;
import org.elasticsearch.xpack.security.authc.support.DelegatedAuthorizationSupport;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.security.authc.support.mapper.CompositeRoleMapper;
import org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
import org.elasticsearch.xpack.security.authc.Realms;
import org.elasticsearch.xpack.security.authc.TokenService;
import org.elasticsearch.xpack.security.authc.support.DelegatedAuthorizationSupport;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper;
import org.opensaml.core.criterion.EntityIdCriterion;
import org.opensaml.saml.common.xml.SAMLConstants;
import org.opensaml.saml.criterion.EntityRoleCriterion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.elasticsearch.xpack.core.security.authc.AuthenticationResult;
import org.elasticsearch.xpack.core.security.authc.AuthenticationToken;
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
import org.elasticsearch.xpack.core.security.authc.support.CachingRealm;
import org.elasticsearch.xpack.core.security.authc.support.CachingUsernamePasswordRealmSettings;
import org.elasticsearch.xpack.core.security.authc.support.Hasher;
import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.core.XPackPlugin;
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
import org.elasticsearch.xpack.core.security.authc.support.CachingRealm;
import org.elasticsearch.xpack.core.security.authc.support.DnRoleMapperSettings;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper;

import java.io.IOException;
import java.nio.file.Files;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
import org.elasticsearch.action.support.GroupedActionListener;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.core.security.authc.RealmConfig;
import org.elasticsearch.xpack.security.authc.support.CachingRealm;
import org.elasticsearch.xpack.core.security.authc.support.CachingRealm;
import org.elasticsearch.xpack.security.authc.support.DnRoleMapper;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper;

/**
* A {@link UserRoleMapper} that composes one or more <i>delegate</i> role-mappers.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
import org.elasticsearch.xpack.core.security.authc.support.mapper.ExpressionRoleMapping;
import org.elasticsearch.xpack.core.security.authc.support.mapper.expressiondsl.ExpressionModel;
import org.elasticsearch.xpack.core.security.index.RestrictedIndicesNames;
import org.elasticsearch.xpack.security.authc.support.CachingRealm;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.core.security.authc.support.CachingRealm;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;

import java.io.IOException;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/

package org.elasticsearch.xpack.security.support;

import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.core.security.SecurityExtension;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper;

/**
* Immutable implementation of {@link SecurityExtension.SecurityComponents}.
*/
public class ExtensionComponents implements SecurityExtension.SecurityComponents {
private final Client client;
private final ThreadPool threadPool;
private final ClusterService clusterService;
private final ResourceWatcherService resourceWatcherService;
private final UserRoleMapper roleMapper;

public ExtensionComponents(Client client, ThreadPool threadPool, ClusterService clusterService,
ResourceWatcherService resourceWatcherService, UserRoleMapper roleMapper) {
this.client = client;
this.threadPool = threadPool;
this.clusterService = clusterService;
this.resourceWatcherService = resourceWatcherService;
this.roleMapper = roleMapper;
}

@Override
public Client client() {
return client;
}

@Override
public ThreadPool threadPool() {
return threadPool;
}

@Override
public ResourceWatcherService resourceWatcherService() {
return resourceWatcherService;
}

@Override
public ClusterService clusterService() {
return clusterService;
}

@Override
public UserRoleMapper roleMapper() {
return roleMapper;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public static class DummyExtension implements SecurityExtension {
}

@Override
public Map<String, Realm.Factory> getRealms(ResourceWatcherService resourceWatcherService) {
public Map<String, Realm.Factory> getRealms(SecurityComponents components) {
return Collections.singletonMap(realmType, config -> null);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
import org.elasticsearch.xpack.security.authc.TokenService;
import org.elasticsearch.xpack.security.authc.oidc.OpenIdConnectRealm;
import org.elasticsearch.xpack.security.authc.oidc.OpenIdConnectTestCase;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.junit.After;
import org.junit.Before;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
import org.elasticsearch.xpack.security.authc.saml.SamlRealm;
import org.elasticsearch.xpack.security.authc.saml.SamlRealmTests;
import org.elasticsearch.xpack.security.authc.saml.SamlTestCase;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.junit.After;
import org.junit.Before;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import org.elasticsearch.xpack.core.security.authc.AuthenticationResult;
import org.elasticsearch.xpack.core.security.authc.kerberos.KerberosRealmSettings;
import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper.UserData;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper.UserData;
import org.ietf.jgss.GSSException;

import javax.security.auth.login.LoginException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
import org.elasticsearch.xpack.core.security.authc.kerberos.KerberosRealmSettings;
import org.elasticsearch.xpack.core.security.support.Exceptions;
import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.security.authc.support.mapper.NativeRoleMappingStore;
import org.elasticsearch.xpack.security.support.SecurityIndexManager;
import org.junit.After;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import org.elasticsearch.xpack.core.security.authc.support.UsernamePasswordToken;
import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.security.authc.support.MockLookupRealm;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper.UserData;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper.UserData;
import org.ietf.jgss.GSSException;

import javax.security.auth.login.LoginException;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
import org.elasticsearch.xpack.core.security.authc.support.DelegatedAuthorizationSettings;
import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.security.authc.support.MockLookupRealm;
import org.elasticsearch.xpack.security.authc.support.UserRoleMapper;
import org.elasticsearch.xpack.core.security.authc.support.UserRoleMapper;
import org.hamcrest.Matchers;
import org.junit.Before;

Expand Down