Extract class to store Authentication in context (#52183)

This change extracts the code that previously existed in the
"Authentication" class that was responsible for reading and writing
authentication objects to/from the ThreadContext.

This is needed to support multiple authentication objects under
separate keys.

This refactoring highlighted that there were a large number of places
where we extracted the Authentication/User objects from the thread
context, in a variety of ways. These have been consolidated to rely on
the SecurityContext object.

Backport of: #52032
This commit is contained in:
Tim Vernum 2020-02-11 20:59:06 +11:00 committed by GitHub
parent 6086fadf00
commit b0b1b13311
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 351 additions and 242 deletions

View File

@ -14,6 +14,7 @@ import org.elasticsearch.common.util.concurrent.ThreadContext.StoredContext;
import org.elasticsearch.node.Node;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType;
import org.elasticsearch.xpack.core.security.authc.support.AuthenticationContextSerializer;
import org.elasticsearch.xpack.core.security.user.User;
import java.io.IOException;
@ -29,17 +30,12 @@ public class SecurityContext {
private final Logger logger = LogManager.getLogger(SecurityContext.class);
private final ThreadContext threadContext;
private final UserSettings userSettings;
private final AuthenticationContextSerializer authenticationSerializer;
private final String nodeName;
/**
* Creates a new security context.
* If cryptoService is null, security is disabled and {@link UserSettings#getUser()}
* and {@link UserSettings#getAuthentication()} will always return null.
*/
public SecurityContext(Settings settings, ThreadContext threadContext) {
this.threadContext = threadContext;
this.userSettings = new UserSettings(threadContext);
this.authenticationSerializer = new AuthenticationContextSerializer();
this.nodeName = Node.NODE_NAME_SETTING.get(settings);
}
@ -52,13 +48,17 @@ public class SecurityContext {
/** Returns the authentication information, or null if the current request has no authentication info. */
public Authentication getAuthentication() {
try {
return Authentication.readFromContext(threadContext);
return authenticationSerializer.readFromContext(threadContext);
} catch (IOException e) {
logger.error("failed to read authentication", e);
throw new UncheckedIOException(e);
}
}
public ThreadContext getThreadContext() {
return threadContext;
}
/**
* Sets the user forcefully to the provided user. There must not be an existing user in the ThreadContext otherwise an exception
* will be thrown. This method is package private for testing.
@ -103,7 +103,7 @@ public class SecurityContext {
*/
public void executeAfterRewritingAuthentication(Consumer<StoredContext> consumer, Version version) {
final StoredContext original = threadContext.newStoredContext(true);
final Authentication authentication = Objects.requireNonNull(userSettings.getAuthentication());
final Authentication authentication = getAuthentication();
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
setAuthentication(new Authentication(authentication.getUser(), authentication.getAuthenticatedBy(),
authentication.getLookedUpBy(), version, authentication.getAuthenticationType(), authentication.getMetadata()));

View File

@ -1,46 +0,0 @@
/*
* 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.core.security;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.user.User;
import java.io.IOException;
public final class UserSettings {
private final Logger logger = LogManager.getLogger(UserSettings.class);
private final ThreadContext threadContext;
UserSettings(ThreadContext threadContext) {
this.threadContext = threadContext;
}
/**
* Returns the current user information, or null if the current request has no authentication info.
*/
public User getUser() {
Authentication authentication = getAuthentication();
return authentication == null ? null : authentication.getUser();
}
/**
* Returns the authentication information, or null if the current request has no authentication info.
*/
public Authentication getAuthentication() {
try {
return Authentication.readFromContext(threadContext);
} catch (IOException e) {
// TODO: this seems bogus, the only way to get an ioexception here is from a corrupt or tampered
// auth header, which should be be audited?
logger.error("failed to read authentication", e);
return null;
}
}
}

View File

@ -13,6 +13,7 @@ import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.xpack.core.security.authc.support.AuthenticationContextSerializer;
import org.elasticsearch.xpack.core.security.user.InternalUserSerializationHelper;
import org.elasticsearch.xpack.core.security.user.User;
@ -93,59 +94,12 @@ public class Authentication implements ToXContentObject {
return metadata;
}
public static Authentication readFromContext(ThreadContext ctx) throws IOException, IllegalArgumentException {
Authentication authentication = ctx.getTransient(AuthenticationField.AUTHENTICATION_KEY);
if (authentication != null) {
assert ctx.getHeader(AuthenticationField.AUTHENTICATION_KEY) != null;
return authentication;
}
String authenticationHeader = ctx.getHeader(AuthenticationField.AUTHENTICATION_KEY);
if (authenticationHeader == null) {
return null;
}
return deserializeHeaderAndPutInContext(authenticationHeader, ctx);
}
public static Authentication getAuthentication(ThreadContext context) {
return context.getTransient(AuthenticationField.AUTHENTICATION_KEY);
}
static Authentication deserializeHeaderAndPutInContext(String header, ThreadContext ctx)
throws IOException, IllegalArgumentException {
assert ctx.getTransient(AuthenticationField.AUTHENTICATION_KEY) == null;
Authentication authentication = decode(header);
ctx.putTransient(AuthenticationField.AUTHENTICATION_KEY, authentication);
return authentication;
}
public static Authentication decode(String header) throws IOException {
byte[] bytes = Base64.getDecoder().decode(header);
StreamInput input = StreamInput.wrap(bytes);
Version version = Version.readVersion(input);
input.setVersion(version);
return new Authentication(input);
}
/**
* Writes the authentication to the context. There must not be an existing authentication in the context and if there is an
* {@link IllegalStateException} will be thrown
*/
public void writeToContext(ThreadContext ctx) throws IOException, IllegalArgumentException {
ensureContextDoesNotContainAuthentication(ctx);
String header = encode();
ctx.putTransient(AuthenticationField.AUTHENTICATION_KEY, this);
ctx.putHeader(AuthenticationField.AUTHENTICATION_KEY, header);
}
void ensureContextDoesNotContainAuthentication(ThreadContext ctx) {
if (ctx.getTransient(AuthenticationField.AUTHENTICATION_KEY) != null) {
if (ctx.getHeader(AuthenticationField.AUTHENTICATION_KEY) == null) {
throw new IllegalStateException("authentication present as a transient but not a header");
}
throw new IllegalStateException("authentication is already present in the context");
}
new AuthenticationContextSerializer().writeToContext(this, ctx);
}
public String encode() throws IOException {

View File

@ -0,0 +1,90 @@
/*
* 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.core.security.authc.support;
import org.elasticsearch.Version;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authc.AuthenticationField;
import java.io.IOException;
import java.util.Base64;
/**
* A class from reading/writing {@link org.elasticsearch.xpack.core.security.authc.Authentication} objects to/from a
* {@link org.elasticsearch.common.util.concurrent.ThreadContext} under a specified key
*/
public class AuthenticationContextSerializer {
private final String contextKey;
public AuthenticationContextSerializer() {
this(AuthenticationField.AUTHENTICATION_KEY);
}
public AuthenticationContextSerializer(String contextKey) {
this.contextKey = contextKey;
}
@Nullable
public Authentication readFromContext(ThreadContext ctx) throws IOException {
Authentication authentication = ctx.getTransient(contextKey);
if (authentication != null) {
assert ctx.getHeader(contextKey) != null;
return authentication;
}
String authenticationHeader = ctx.getHeader(contextKey);
if (authenticationHeader == null) {
return null;
}
return deserializeHeaderAndPutInContext(authenticationHeader, ctx);
}
Authentication deserializeHeaderAndPutInContext(String headerValue, ThreadContext ctx)
throws IOException, IllegalArgumentException {
assert ctx.getTransient(contextKey) == null;
Authentication authentication = decode(headerValue);
ctx.putTransient(contextKey, authentication);
return authentication;
}
public static Authentication decode(String header) throws IOException {
byte[] bytes = Base64.getDecoder().decode(header);
StreamInput input = StreamInput.wrap(bytes);
Version version = Version.readVersion(input);
input.setVersion(version);
return new Authentication(input);
}
public Authentication getAuthentication(ThreadContext context) {
return context.getTransient(contextKey);
}
/**
* Writes the authentication to the context. There must not be an existing authentication in the context and if there is an
* {@link IllegalStateException} will be thrown
*/
public void writeToContext(Authentication authentication, ThreadContext ctx) throws IOException {
ensureContextDoesNotContainAuthentication(ctx);
String header = authentication.encode();
ctx.putTransient(contextKey, authentication);
ctx.putHeader(contextKey, header);
}
void ensureContextDoesNotContainAuthentication(ThreadContext ctx) {
if (ctx.getTransient(contextKey) != null) {
if (ctx.getHeader(contextKey) == null) {
throw new IllegalStateException("authentication present as a transient ([" + contextKey + "]) but not a header");
}
throw new IllegalStateException("authentication ([" + contextKey + "]) is already present in the context");
}
}
}

View File

@ -19,13 +19,14 @@ import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.index.shard.ShardUtils;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.authz.AuthorizationServiceField;
import org.elasticsearch.xpack.core.security.authz.permission.DocumentPermissions;
import org.elasticsearch.xpack.core.security.support.Exceptions;
import org.elasticsearch.xpack.core.security.user.User;
import java.io.IOException;
import java.util.Objects;
import java.util.function.Function;
/**
@ -45,16 +46,16 @@ public class SecurityIndexReaderWrapper implements CheckedFunction<DirectoryRead
private final Function<ShardId, QueryShardContext> queryShardContextProvider;
private final DocumentSubsetBitsetCache bitsetCache;
private final XPackLicenseState licenseState;
private final ThreadContext threadContext;
private final SecurityContext securityContext;
private final ScriptService scriptService;
public SecurityIndexReaderWrapper(Function<ShardId, QueryShardContext> queryShardContextProvider,
DocumentSubsetBitsetCache bitsetCache, ThreadContext threadContext, XPackLicenseState licenseState,
ScriptService scriptService) {
DocumentSubsetBitsetCache bitsetCache, SecurityContext securityContext,
XPackLicenseState licenseState, ScriptService scriptService) {
this.scriptService = scriptService;
this.queryShardContextProvider = queryShardContextProvider;
this.bitsetCache = bitsetCache;
this.threadContext = threadContext;
this.securityContext = securityContext;
this.licenseState = licenseState;
}
@ -95,6 +96,7 @@ public class SecurityIndexReaderWrapper implements CheckedFunction<DirectoryRead
}
protected IndicesAccessControl getIndicesAccessControl() {
final ThreadContext threadContext = securityContext.getThreadContext();
IndicesAccessControl indicesAccessControl = threadContext.getTransient(AuthorizationServiceField.INDICES_PERMISSIONS_KEY);
if (indicesAccessControl == null) {
throw Exceptions.authorizationError("no indices permissions found");
@ -102,9 +104,8 @@ public class SecurityIndexReaderWrapper implements CheckedFunction<DirectoryRead
return indicesAccessControl;
}
protected User getUser(){
Authentication authentication = Authentication.getAuthentication(threadContext);
return authentication.getUser();
protected User getUser() {
return Objects.requireNonNull(securityContext.getUser());
}
}

View File

@ -10,6 +10,7 @@ import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authc.AuthenticationField;
import org.elasticsearch.xpack.core.security.authc.support.AuthenticationContextSerializer;
import org.elasticsearch.xpack.core.watcher.actions.ActionWrapperResult;
import org.elasticsearch.xpack.core.watcher.condition.Condition;
import org.elasticsearch.xpack.core.watcher.history.WatchRecord;
@ -262,7 +263,7 @@ public abstract class WatchExecutionContext {
if (watch != null && watch.status() != null && watch.status().getHeaders() != null) {
String header = watch.status().getHeaders().get(AuthenticationField.AUTHENTICATION_KEY);
if (header != null) {
Authentication auth = Authentication.decode(header);
Authentication auth = AuthenticationContextSerializer.decode(header);
return auth.getUser().principal();
}
}

View File

@ -39,8 +39,9 @@ import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.internal.ContextIndexSearcher;
import org.elasticsearch.test.AbstractBuilderTestCase;
import org.elasticsearch.test.IndexSettingsModule;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authc.AuthenticationField;
import org.elasticsearch.xpack.core.security.authc.support.AuthenticationContextSerializer;
import org.elasticsearch.xpack.core.security.authz.permission.DocumentPermissions;
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissions;
import org.elasticsearch.xpack.core.security.user.User;
@ -69,10 +70,14 @@ public class SecurityIndexReaderWrapperIntegrationTests extends AbstractBuilderT
when(mapperService.simpleMatchToFullName(anyString()))
.then(invocationOnMock -> Collections.singletonList((String) invocationOnMock.getArguments()[0]));
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
final ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
final Authentication authentication = mock(Authentication.class);
when(authentication.getUser()).thenReturn(mock(User.class));
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, authentication);
when(authentication.encode()).thenReturn(randomAlphaOfLength(24)); // don't care as long as it's not null
new AuthenticationContextSerializer().writeToContext(authentication, threadContext);
IndexSettings indexSettings = IndexSettingsModule.newIndexSettings(shardId.getIndex(), Settings.EMPTY);
Client client = mock(Client.class);
when(client.settings()).thenReturn(Settings.EMPTY);
@ -135,7 +140,7 @@ public class SecurityIndexReaderWrapperIntegrationTests extends AbstractBuilderT
FieldPermissions(),
DocumentPermissions.filteredBy(singleton(new BytesArray(termQuery))));
SecurityIndexReaderWrapper wrapper = new SecurityIndexReaderWrapper(s -> queryShardContext,
bitsetCache, threadContext, licenseState, scriptService) {
bitsetCache, securityContext, licenseState, scriptService) {
@Override
protected IndicesAccessControl getIndicesAccessControl() {
@ -173,10 +178,13 @@ public class SecurityIndexReaderWrapperIntegrationTests extends AbstractBuilderT
when(mapperService.simpleMatchToFullName(anyString()))
.then(invocationOnMock -> Collections.singletonList((String) invocationOnMock.getArguments()[0]));
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
final ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
final Authentication authentication = mock(Authentication.class);
when(authentication.getUser()).thenReturn(mock(User.class));
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, authentication);
when(authentication.encode()).thenReturn(randomAlphaOfLength(24)); // don't care as long as it's not null
new AuthenticationContextSerializer().writeToContext(authentication, threadContext);
final boolean noFilteredIndexPermissions = randomBoolean();
boolean restrictiveLimitedIndexPermissions = false;
if (noFilteredIndexPermissions == false) {
@ -208,7 +216,7 @@ public class SecurityIndexReaderWrapperIntegrationTests extends AbstractBuilderT
XPackLicenseState licenseState = mock(XPackLicenseState.class);
when(licenseState.isDocumentAndFieldLevelSecurityAllowed()).thenReturn(true);
SecurityIndexReaderWrapper wrapper = new SecurityIndexReaderWrapper(s -> queryShardContext,
bitsetCache, threadContext, licenseState, scriptService) {
bitsetCache, securityContext, licenseState, scriptService) {
@Override
protected IndicesAccessControl getIndicesAccessControl() {

View File

@ -22,6 +22,7 @@ import org.elasticsearch.index.shard.ShardId;
import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.authz.permission.DocumentPermissions;
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissions;
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsDefinition;
@ -50,7 +51,7 @@ public class SecurityIndexReaderWrapperUnitTests extends ESTestCase {
META_FIELDS = Collections.unmodifiableSet(metaFields);
}
private ThreadContext threadContext;
private SecurityContext securityContext;
private ScriptService scriptService;
private SecurityIndexReaderWrapper securityIndexReaderWrapper;
private ElasticsearchDirectoryReader esIn;
@ -64,7 +65,7 @@ public class SecurityIndexReaderWrapperUnitTests extends ESTestCase {
ShardId shardId = new ShardId(index, 0);
licenseState = mock(XPackLicenseState.class);
when(licenseState.isDocumentAndFieldLevelSecurityAllowed()).thenReturn(true);
threadContext = new ThreadContext(Settings.EMPTY);
securityContext = new SecurityContext(Settings.EMPTY, new ThreadContext(Settings.EMPTY));
IndexShard indexShard = mock(IndexShard.class);
when(indexShard.shardId()).thenReturn(shardId);
@ -84,7 +85,7 @@ public class SecurityIndexReaderWrapperUnitTests extends ESTestCase {
public void testDefaultMetaFields() throws Exception {
securityIndexReaderWrapper =
new SecurityIndexReaderWrapper(null, null, threadContext, licenseState, scriptService) {
new SecurityIndexReaderWrapper(null, null, securityContext, licenseState, scriptService) {
@Override
protected IndicesAccessControl getIndicesAccessControl() {
IndicesAccessControl.IndexAccessControl indexAccessControl = new IndicesAccessControl.IndexAccessControl(true,
@ -114,7 +115,7 @@ public class SecurityIndexReaderWrapperUnitTests extends ESTestCase {
public void testWrapReaderWhenFeatureDisabled() throws Exception {
when(licenseState.isDocumentAndFieldLevelSecurityAllowed()).thenReturn(false);
securityIndexReaderWrapper =
new SecurityIndexReaderWrapper(null, null, threadContext, licenseState, scriptService);
new SecurityIndexReaderWrapper(null, null, securityContext, licenseState, scriptService);
DirectoryReader reader = securityIndexReaderWrapper.apply(esIn);
assertThat(reader, sameInstance(esIn));
}
@ -148,7 +149,7 @@ public class SecurityIndexReaderWrapperUnitTests extends ESTestCase {
public void testFieldPermissionsWithFieldExceptions() throws Exception {
securityIndexReaderWrapper =
new SecurityIndexReaderWrapper(null, null, threadContext, licenseState, null);
new SecurityIndexReaderWrapper(null, null, securityContext, licenseState, null);
String[] grantedFields = new String[]{};
String[] deniedFields;
Set<String> expected = new HashSet<>(META_FIELDS);

View File

@ -427,8 +427,8 @@ public class Security extends Plugin implements SystemIndexPlugin, IngestPlugin,
securityIndex.set(SecurityIndexManager.buildSecurityMainIndexManager(client, clusterService));
final TokenService tokenService = new TokenService(settings, Clock.systemUTC(), client, getLicenseState(),
securityIndex.get(), SecurityIndexManager.buildSecurityTokensIndexManager(client, clusterService), clusterService);
final TokenService tokenService = new TokenService(settings, Clock.systemUTC(), client, getLicenseState(), securityContext.get(),
securityIndex.get(), SecurityIndexManager.buildSecurityTokensIndexManager(client, clusterService), clusterService);
this.tokenService.set(tokenService);
components.add(tokenService);
@ -726,7 +726,8 @@ public class Security extends Plugin implements SystemIndexPlugin, IngestPlugin,
}, null),
dlsBitsetCache.get(),
indexService.getThreadPool().getThreadContext(), getLicenseState(),
securityContext.get(),
getLicenseState(),
indexService.getScriptService()));
/*
* We need to forcefully overwrite the query cache implementation to use security's opt-out query cache implementation. This
@ -746,7 +747,7 @@ public class Security extends Plugin implements SystemIndexPlugin, IngestPlugin,
// attaches information to the scroll context so that we can validate the user that created the scroll against
// the user that is executing a scroll operation
module.addSearchOperationListener(
new SecuritySearchOperationListener(threadContext.get(), getLicenseState(), auditTrailService.get()));
new SecuritySearchOperationListener(securityContext.get(), getLicenseState(), auditTrailService.get()));
}
}
@ -854,7 +855,7 @@ public class Security extends Plugin implements SystemIndexPlugin, IngestPlugin,
@Override
public Map<String, Processor.Factory> getProcessors(Processor.Parameters parameters) {
return Collections.singletonMap(SetSecurityUserProcessor.TYPE, new SetSecurityUserProcessor.Factory(parameters.threadContext));
return Collections.singletonMap(SetSecurityUserProcessor.TYPE, new SetSecurityUserProcessor.Factory(securityContext::get));
}
/**

View File

@ -19,6 +19,7 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationAction;
import org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationRequest;
import org.elasticsearch.xpack.core.security.action.DelegatePkiAuthenticationResponse;
@ -38,7 +39,7 @@ import java.util.Collections;
* {@code PkiRealmSettings#DELEGATION_ENABLED_SETTING} set to {@code true} (default is {@code false}). A successfully trusted target
* certificate is also subject to the validation of the subject distinguished name according to that respective's realm
* {@code PkiRealmSettings#USERNAME_PATTERN_SETTING}.
*
*
* IMPORTANT: The association between the subject public key in the target certificate and the corresponding private key is <b>not</b>
* validated. This is part of the TLS authentication process and it is delegated to the proxy calling this API. The proxy is <b>trusted</b>
* to have performed the TLS authentication, and this API translates that authentication into an Elasticsearch access token.
@ -51,21 +52,24 @@ public final class TransportDelegatePkiAuthenticationAction
private final ThreadPool threadPool;
private final AuthenticationService authenticationService;
private final TokenService tokenService;
private final SecurityContext securityContext;
@Inject
public TransportDelegatePkiAuthenticationAction(ThreadPool threadPool, TransportService transportService, ActionFilters actionFilters,
AuthenticationService authenticationService, TokenService tokenService) {
AuthenticationService authenticationService, TokenService tokenService,
SecurityContext securityContext) {
super(DelegatePkiAuthenticationAction.NAME, transportService, actionFilters, DelegatePkiAuthenticationRequest::new);
this.threadPool = threadPool;
this.authenticationService = authenticationService;
this.tokenService = tokenService;
this.securityContext = securityContext;
}
@Override
protected void doExecute(Task task, DelegatePkiAuthenticationRequest request,
ActionListener<DelegatePkiAuthenticationResponse> listener) {
final ThreadContext threadContext = threadPool.getThreadContext();
Authentication delegateeAuthentication = Authentication.getAuthentication(threadContext);
Authentication delegateeAuthentication = securityContext.getAuthentication();
if (delegateeAuthentication == null) {
listener.onFailure(new IllegalStateException("Delegatee authentication cannot be null"));
return;

View File

@ -20,6 +20,7 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.action.oidc.OpenIdConnectAuthenticateRequest;
import org.elasticsearch.xpack.core.security.action.oidc.OpenIdConnectAuthenticateResponse;
import org.elasticsearch.xpack.core.security.action.oidc.OpenIdConnectAuthenticateAction;
@ -38,17 +39,19 @@ public class TransportOpenIdConnectAuthenticateAction
private final ThreadPool threadPool;
private final AuthenticationService authenticationService;
private final TokenService tokenService;
private final SecurityContext securityContext;
private static final Logger logger = LogManager.getLogger(TransportOpenIdConnectAuthenticateAction.class);
@Inject
public TransportOpenIdConnectAuthenticateAction(ThreadPool threadPool, TransportService transportService,
ActionFilters actionFilters, AuthenticationService authenticationService,
TokenService tokenService) {
TokenService tokenService, SecurityContext securityContext) {
super(OpenIdConnectAuthenticateAction.NAME, transportService, actionFilters,
(Writeable.Reader<OpenIdConnectAuthenticateRequest>) OpenIdConnectAuthenticateRequest::new);
this.threadPool = threadPool;
this.authenticationService = authenticationService;
this.tokenService = tokenService;
this.securityContext = securityContext;
}
@Override
@ -57,7 +60,7 @@ public class TransportOpenIdConnectAuthenticateAction
final OpenIdConnectToken token = new OpenIdConnectToken(request.getRedirectUri(), new State(request.getState()),
new Nonce(request.getNonce()), request.getRealm());
final ThreadContext threadContext = threadPool.getThreadContext();
Authentication originatingAuthentication = Authentication.getAuthentication(threadContext);
Authentication originatingAuthentication = securityContext.getAuthentication();
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
authenticationService.authenticate(OpenIdConnectAuthenticateAction.NAME, request, token, ActionListener.wrap(
authentication -> {

View File

@ -15,6 +15,7 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.action.saml.SamlAuthenticateAction;
import org.elasticsearch.xpack.core.security.action.saml.SamlAuthenticateRequest;
import org.elasticsearch.xpack.core.security.action.saml.SamlAuthenticateResponse;
@ -35,15 +36,17 @@ public final class TransportSamlAuthenticateAction extends HandledTransportActio
private final ThreadPool threadPool;
private final AuthenticationService authenticationService;
private final TokenService tokenService;
private final SecurityContext securityContext;
@Inject
public TransportSamlAuthenticateAction(ThreadPool threadPool, TransportService transportService,
ActionFilters actionFilters, AuthenticationService authenticationService,
TokenService tokenService) {
TokenService tokenService, SecurityContext securityContext) {
super(SamlAuthenticateAction.NAME, transportService, actionFilters, SamlAuthenticateRequest::new);
this.threadPool = threadPool;
this.authenticationService = authenticationService;
this.tokenService = tokenService;
this.securityContext = securityContext;
}
@Override
@ -51,7 +54,7 @@ public final class TransportSamlAuthenticateAction extends HandledTransportActio
final SamlToken saml = new SamlToken(request.getSaml(), request.getValidRequestIds(), request.getRealm());
logger.trace("Attempting to authenticate SamlToken [{}]", saml);
final ThreadContext threadContext = threadPool.getThreadContext();
Authentication originatingAuthentication = Authentication.getAuthentication(threadContext);
Authentication originatingAuthentication = securityContext.getAuthentication();
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
authenticationService.authenticate(SamlAuthenticateAction.NAME, request, saml, ActionListener.wrap(authentication -> {
AuthenticationResult result = threadContext.getTransient(AuthenticationResult.THREAD_CONTEXT_KEY);

View File

@ -14,6 +14,7 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.action.token.CreateTokenAction;
import org.elasticsearch.xpack.core.security.action.token.CreateTokenRequest;
import org.elasticsearch.xpack.core.security.action.token.CreateTokenRequest.GrantType;
@ -41,14 +42,17 @@ public final class TransportCreateTokenAction extends HandledTransportAction<Cre
private final ThreadPool threadPool;
private final TokenService tokenService;
private final AuthenticationService authenticationService;
private final SecurityContext securityContext;
@Inject
public TransportCreateTokenAction(ThreadPool threadPool, TransportService transportService, ActionFilters actionFilters,
TokenService tokenService, AuthenticationService authenticationService) {
TokenService tokenService, AuthenticationService authenticationService,
SecurityContext securityContext) {
super(CreateTokenAction.NAME, transportService, actionFilters, CreateTokenRequest::new);
this.threadPool = threadPool;
this.tokenService = tokenService;
this.authenticationService = authenticationService;
this.securityContext = securityContext;
}
@Override
@ -61,7 +65,7 @@ public final class TransportCreateTokenAction extends HandledTransportAction<Cre
authenticateAndCreateToken(type, request, listener);
break;
case CLIENT_CREDENTIALS:
Authentication authentication = Authentication.getAuthentication(threadPool.getThreadContext());
Authentication authentication = securityContext.getAuthentication();
createToken(type, request, authentication, authentication, false, listener);
break;
default:
@ -72,7 +76,7 @@ public final class TransportCreateTokenAction extends HandledTransportAction<Cre
}
private void authenticateAndCreateToken(GrantType grantType, CreateTokenRequest request, ActionListener<CreateTokenResponse> listener) {
Authentication originatingAuthentication = Authentication.getAuthentication(threadPool.getThreadContext());
Authentication originatingAuthentication = securityContext.getAuthentication();
try (ThreadContext.StoredContext ignore = threadPool.getThreadContext().stashContext()) {
final AuthenticationToken authToken = extractAuthenticationToken(grantType, request, listener);
if (authToken == null) {

View File

@ -12,6 +12,7 @@ import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.action.user.GetUserPrivilegesAction;
import org.elasticsearch.xpack.core.security.action.user.GetUserPrivilegesRequest;
import org.elasticsearch.xpack.core.security.action.user.GetUserPrivilegesResponse;
@ -26,21 +27,28 @@ public class TransportGetUserPrivilegesAction extends HandledTransportAction<Get
private final ThreadPool threadPool;
private final AuthorizationService authorizationService;
private final SecurityContext securityContext;
@Inject
public TransportGetUserPrivilegesAction(ThreadPool threadPool, TransportService transportService,
ActionFilters actionFilters, AuthorizationService authorizationService) {
ActionFilters actionFilters, AuthorizationService authorizationService,
SecurityContext securityContext) {
super(GetUserPrivilegesAction.NAME, transportService, actionFilters, GetUserPrivilegesRequest::new);
this.threadPool = threadPool;
this.authorizationService = authorizationService;
this.securityContext = securityContext;
}
@Override
protected void doExecute(Task task, GetUserPrivilegesRequest request, ActionListener<GetUserPrivilegesResponse> listener) {
final String username = request.username();
final Authentication authentication = Authentication.getAuthentication(threadPool.getThreadContext());
final User user = authentication.getUser();
final Authentication authentication = securityContext.getAuthentication();
final User user = securityContext.getUser();
if (authentication == null || user == null) {
listener.onFailure(new IllegalArgumentException("cannot list privileges as there is no active user"));
return;
}
if (user.principal().equals(username) == false) {
listener.onFailure(new IllegalArgumentException("users may only list the privileges of their own account"));
return;

View File

@ -58,8 +58,7 @@ public class TransportHasPrivilegesAction extends HandledTransportAction<HasPriv
@Override
protected void doExecute(Task task, HasPrivilegesRequest request, ActionListener<HasPrivilegesResponse> listener) {
final String username = request.username();
final Authentication authentication = Authentication.getAuthentication(threadPool.getThreadContext());
final Authentication authentication = securityContext.getAuthentication();
final User user = authentication.getUser();
if (user.principal().equals(username) == false) {
listener.onFailure(new IllegalArgumentException("users may only check the privileges of their own account"));

View File

@ -13,10 +13,10 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.action.user.SetEnabledAction;
import org.elasticsearch.xpack.core.security.action.user.SetEnabledRequest;
import org.elasticsearch.xpack.core.security.action.user.SetEnabledResponse;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.user.AnonymousUser;
import org.elasticsearch.xpack.core.security.user.SystemUser;
import org.elasticsearch.xpack.core.security.user.XPackUser;
@ -29,14 +29,16 @@ public class TransportSetEnabledAction extends HandledTransportAction<SetEnabled
private final Settings settings;
private final ThreadPool threadPool;
private final SecurityContext securityContext;
private final NativeUsersStore usersStore;
@Inject
public TransportSetEnabledAction(Settings settings, ThreadPool threadPool, TransportService transportService,
ActionFilters actionFilters, NativeUsersStore usersStore) {
ActionFilters actionFilters, SecurityContext securityContext, NativeUsersStore usersStore) {
super(SetEnabledAction.NAME, transportService, actionFilters, SetEnabledRequest::new);
this.settings = settings;
this.threadPool = threadPool;
this.securityContext = securityContext;
this.usersStore = usersStore;
}
@ -44,7 +46,7 @@ public class TransportSetEnabledAction extends HandledTransportAction<SetEnabled
protected void doExecute(Task task, SetEnabledRequest request, ActionListener<SetEnabledResponse> listener) {
final String username = request.username();
// make sure the user is not disabling themselves
if (Authentication.getAuthentication(threadPool.getThreadContext()).getUser().principal().equals(request.username())) {
if (securityContext.getUser().principal().equals(request.username())) {
listener.onFailure(new IllegalArgumentException("users may not update the enabled status of their own account"));
return;
} else if (SystemUser.NAME.equals(username) || XPackUser.NAME.equals(username)) {

View File

@ -36,6 +36,7 @@ import org.elasticsearch.xpack.core.security.authc.AuthenticationResult;
import org.elasticsearch.xpack.core.security.authc.AuthenticationServiceField;
import org.elasticsearch.xpack.core.security.authc.AuthenticationToken;
import org.elasticsearch.xpack.core.security.authc.Realm;
import org.elasticsearch.xpack.core.security.authc.support.AuthenticationContextSerializer;
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.EmptyAuthorizationInfo;
import org.elasticsearch.xpack.core.security.support.Exceptions;
import org.elasticsearch.xpack.core.security.user.AnonymousUser;
@ -87,6 +88,7 @@ public class AuthenticationService {
private final ApiKeyService apiKeyService;
private final boolean runAsEnabled;
private final boolean isAnonymousUserEnabled;
private final AuthenticationContextSerializer authenticationSerializer;
public AuthenticationService(Settings settings, Realms realms, AuditTrailService auditTrail,
AuthenticationFailureHandler failureHandler, ThreadPool threadPool,
@ -109,6 +111,7 @@ public class AuthenticationService {
this.lastSuccessfulAuthCache = null;
}
this.apiKeyService = apiKeyService;
this.authenticationSerializer = new AuthenticationContextSerializer();
}
/**
@ -303,7 +306,7 @@ public class AuthenticationService {
private void lookForExistingAuthentication(Consumer<Authentication> authenticationConsumer) {
Runnable action;
try {
final Authentication authentication = Authentication.readFromContext(threadContext);
final Authentication authentication = authenticationSerializer.readFromContext(threadContext);
if (authentication != null && request instanceof AuditableRestRequest) {
action = () -> listener.onFailure(request.tamperedRequest());
} else {
@ -611,7 +614,7 @@ public class AuthenticationService {
listener.onResponse(authentication);
};
try {
authentication.writeToContext(threadContext);
authenticationSerializer.writeToContext(authentication, threadContext);
} catch (Exception e) {
action = () -> {
logger.debug(

View File

@ -82,6 +82,7 @@ import org.elasticsearch.xpack.core.XPackField;
import org.elasticsearch.xpack.core.XPackPlugin;
import org.elasticsearch.xpack.core.XPackSettings;
import org.elasticsearch.xpack.core.security.ScrollHelper;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType;
import org.elasticsearch.xpack.core.security.authc.KeyAndTimestamp;
@ -208,6 +209,7 @@ public final class TokenService {
private final ExpiredTokenRemover expiredTokenRemover;
private final boolean enabled;
private final XPackLicenseState licenseState;
private final SecurityContext securityContext;
private volatile TokenKeys keyCache;
private volatile long lastExpirationRunMs;
private final AtomicLong createdTimeStamps = new AtomicLong(-1);
@ -215,7 +217,7 @@ public final class TokenService {
/**
* Creates a new token service
*/
public TokenService(Settings settings, Clock clock, Client client, XPackLicenseState licenseState,
public TokenService(Settings settings, Clock clock, Client client, XPackLicenseState licenseState, SecurityContext securityContext,
SecurityIndexManager securityMainIndex, SecurityIndexManager securityTokensIndex,
ClusterService clusterService) throws GeneralSecurityException {
byte[] saltArr = new byte[SALT_BYTES];
@ -226,6 +228,7 @@ public final class TokenService {
this.expirationDelay = TOKEN_EXPIRATION.get(settings);
this.client = client;
this.licenseState = licenseState;
this.securityContext = securityContext;
this.securityMainIndex = securityMainIndex;
this.securityTokensIndex = securityTokensIndex;
this.lastExpirationRunMs = client.threadPool().relativeTimeInMillis();
@ -801,7 +804,7 @@ public final class TokenService {
findTokenFromRefreshToken(refreshToken,
backoff,
ActionListener.wrap(tokenDocHit -> {
final Authentication clientAuth = Authentication.readFromContext(client.threadPool().getThreadContext());
final Authentication clientAuth = securityContext.getAuthentication();
innerRefresh(refreshToken, tokenDocHit.getId(), tokenDocHit.getSourceAsMap(), tokenDocHit.getSeqNo(),
tokenDocHit.getPrimaryTerm(),
clientAuth, backoff, refreshRequested, listener);

View File

@ -12,11 +12,12 @@ import org.elasticsearch.search.SearchContextMissingException;
import org.elasticsearch.search.internal.ScrollContext;
import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authc.AuthenticationField;
import org.elasticsearch.xpack.security.audit.AuditUtil;
import org.elasticsearch.xpack.core.security.authz.AuthorizationEngine.AuthorizationInfo;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.audit.AuditUtil;
import static org.elasticsearch.xpack.security.authz.AuthorizationService.AUTHORIZATION_INFO_KEY;
import static org.elasticsearch.xpack.security.authz.AuthorizationService.ORIGINATING_ACTION_KEY;
@ -32,12 +33,12 @@ import static org.elasticsearch.xpack.security.authz.AuthorizationService.ORIGIN
*/
public final class SecuritySearchOperationListener implements SearchOperationListener {
private final ThreadContext threadContext;
private final SecurityContext securityContext;
private final XPackLicenseState licenseState;
private final AuditTrailService auditTrailService;
public SecuritySearchOperationListener(ThreadContext threadContext, XPackLicenseState licenseState, AuditTrailService auditTrail) {
this.threadContext = threadContext;
public SecuritySearchOperationListener(SecurityContext securityContext, XPackLicenseState licenseState, AuditTrailService auditTrail) {
this.securityContext = securityContext;
this.licenseState = licenseState;
this.auditTrailService = auditTrail;
}
@ -48,8 +49,7 @@ public final class SecuritySearchOperationListener implements SearchOperationLis
@Override
public void onNewScrollContext(SearchContext searchContext) {
if (licenseState.isAuthAllowed()) {
searchContext.scrollContext().putInContext(AuthenticationField.AUTHENTICATION_KEY,
Authentication.getAuthentication(threadContext));
searchContext.scrollContext().putInContext(AuthenticationField.AUTHENTICATION_KEY, securityContext.getAuthentication());
}
}
@ -62,7 +62,8 @@ public final class SecuritySearchOperationListener implements SearchOperationLis
if (licenseState.isAuthAllowed()) {
if (searchContext.scrollContext() != null) {
final Authentication originalAuth = searchContext.scrollContext().getFromContext(AuthenticationField.AUTHENTICATION_KEY);
final Authentication current = Authentication.getAuthentication(threadContext);
final Authentication current = securityContext.getAuthentication();
final ThreadContext threadContext = securityContext.getThreadContext();
final String action = threadContext.getTransient(ORIGINATING_ACTION_KEY);
ensureAuthenticatedUserIsSame(originalAuth, current, auditTrailService, searchContext.id(), action, request,
AuditUtil.extractRequestId(threadContext), threadContext.getTransient(AUTHORIZATION_INFO_KEY));

View File

@ -5,10 +5,10 @@
*/
package org.elasticsearch.xpack.security.ingest;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.ingest.AbstractProcessor;
import org.elasticsearch.ingest.IngestDocument;
import org.elasticsearch.ingest.Processor;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.user.User;
@ -18,7 +18,9 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;
import static org.elasticsearch.ingest.ConfigurationUtils.newConfigurationException;
import static org.elasticsearch.ingest.ConfigurationUtils.readOptionalList;
@ -31,20 +33,21 @@ public final class SetSecurityUserProcessor extends AbstractProcessor {
public static final String TYPE = "set_security_user";
private final ThreadContext threadContext;
private final SecurityContext securityContext;
private final String field;
private final Set<Property> properties;
public SetSecurityUserProcessor(String tag, ThreadContext threadContext, String field, Set<Property> properties) {
public
SetSecurityUserProcessor(String tag, SecurityContext securityContext, String field, Set<Property> properties) {
super(tag);
this.threadContext = threadContext;
this.securityContext = Objects.requireNonNull(securityContext, "security context must be provided");
this.field = field;
this.properties = properties;
}
@Override
public IngestDocument execute(IngestDocument ingestDocument) throws Exception {
Authentication authentication = Authentication.getAuthentication(threadContext);
Authentication authentication = securityContext.getAuthentication();
if (authentication == null) {
throw new IllegalStateException("No user authenticated, only use this processor via authenticated user");
}
@ -108,10 +111,10 @@ public final class SetSecurityUserProcessor extends AbstractProcessor {
public static final class Factory implements Processor.Factory {
private final ThreadContext threadContext;
private final Supplier<SecurityContext> securityContext;
public Factory(ThreadContext threadContext) {
this.threadContext = threadContext;
public Factory(Supplier<SecurityContext> securityContext) {
this.securityContext = securityContext;
}
@Override
@ -128,7 +131,7 @@ public final class SetSecurityUserProcessor extends AbstractProcessor {
} else {
properties = EnumSet.allOf(Property.class);
}
return new SetSecurityUserProcessor(tag, threadContext, field, properties);
return new SetSecurityUserProcessor(tag, securityContext.get(), field, properties);
}
}

View File

@ -123,7 +123,7 @@ public interface ServerTransportFilter {
if (securityAction.equals(TransportService.HANDSHAKE_ACTION_NAME) &&
SystemUser.is(authentication.getUser()) == false) {
securityContext.executeAsUser(SystemUser.INSTANCE, (ctx) -> {
final Authentication replaced = Authentication.getAuthentication(threadContext);
final Authentication replaced = securityContext.getAuthentication();
authzService.authorize(replaced, securityAction, request, listener);
}, version);
} else {

View File

@ -74,7 +74,7 @@ public class SecurityContextTests extends ESTestCase {
IllegalStateException e = expectThrows(IllegalStateException.class,
() -> securityContext.setUser(randomFrom(user, SystemUser.INSTANCE), Version.CURRENT));
assertEquals("authentication is already present in the context", e.getMessage());
assertEquals("authentication ([_xpack_security_authentication]) is already present in the context", e.getMessage());
}
public void testExecuteAsUser() throws IOException {

View File

@ -40,6 +40,7 @@ import org.elasticsearch.transport.Transport;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.core.XPackSettings;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.action.oidc.OpenIdConnectLogoutRequest;
import org.elasticsearch.xpack.core.security.action.oidc.OpenIdConnectLogoutResponse;
import org.elasticsearch.xpack.core.security.authc.Authentication;
@ -177,8 +178,8 @@ public class TransportOpenIdConnectLogoutActionTests extends OpenIdConnectTestCa
final XPackLicenseState licenseState = mock(XPackLicenseState.class);
when(licenseState.isTokenServiceAllowed()).thenReturn(true);
tokenService = new TokenService(settings, Clock.systemUTC(), client, licenseState,
securityIndex, securityIndex, clusterService);
tokenService = new TokenService(settings, Clock.systemUTC(), client, licenseState, new SecurityContext(settings, threadContext),
securityIndex, securityIndex, clusterService);
final TransportService transportService = new TransportService(Settings.EMPTY, mock(Transport.class), null,
TransportService.NOOP_TRANSPORT_INTERCEPTOR, x -> null, null, Collections.emptySet());

View File

@ -57,6 +57,7 @@ import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.XPackSettings;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.action.saml.SamlInvalidateSessionRequest;
import org.elasticsearch.xpack.core.security.action.saml.SamlInvalidateSessionResponse;
import org.elasticsearch.xpack.core.security.authc.Authentication;
@ -206,7 +207,9 @@ public class TransportSamlInvalidateSessionActionTests extends SamlTestCase {
when(licenseState.isTokenServiceAllowed()).thenReturn(true);
final ClusterService clusterService = ClusterServiceUtils.createClusterService(threadPool);
tokenService = new TokenService(settings, Clock.systemUTC(), client, licenseState, securityIndex, securityIndex, clusterService);
final SecurityContext securityContext = new SecurityContext(settings, threadContext);
tokenService = new TokenService(settings, Clock.systemUTC(), client, licenseState, securityContext, securityIndex, securityIndex,
clusterService);
final TransportService transportService = new TransportService(Settings.EMPTY, mock(Transport.class), null,
TransportService.NOOP_TRANSPORT_INTERCEPTOR, x -> null, null, Collections.emptySet());

View File

@ -47,6 +47,7 @@ import org.elasticsearch.transport.Transport;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.core.XPackSettings;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.action.saml.SamlLogoutRequest;
import org.elasticsearch.xpack.core.security.action.saml.SamlLogoutResponse;
import org.elasticsearch.xpack.core.security.authc.Authentication;
@ -207,7 +208,9 @@ public class TransportSamlLogoutActionTests extends SamlTestCase {
final XPackLicenseState licenseState = mock(XPackLicenseState.class);
when(licenseState.isTokenServiceAllowed()).thenReturn(true);
final ClusterService clusterService = ClusterServiceUtils.createClusterService(threadPool);
tokenService = new TokenService(settings, Clock.systemUTC(), client, licenseState, securityIndex, securityIndex, clusterService);
final SecurityContext securityContext = new SecurityContext(settings, threadContext);
tokenService = new TokenService(settings, Clock.systemUTC(), client, licenseState, securityContext, securityIndex, securityIndex,
clusterService);
final TransportService transportService = new TransportService(Settings.EMPTY, mock(Transport.class), null,
TransportService.NOOP_TRANSPORT_INTERCEPTOR, x -> null, null, Collections.emptySet());

View File

@ -39,6 +39,7 @@ import org.elasticsearch.threadpool.TestThreadPool;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.XPackSettings;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.action.token.CreateTokenAction;
import org.elasticsearch.xpack.core.security.action.token.CreateTokenRequest;
import org.elasticsearch.xpack.core.security.action.token.CreateTokenResponse;
@ -81,6 +82,7 @@ public class TransportCreateTokenActionTests extends ESTestCase {
private AtomicReference<IndexRequest> idxReqReference;
private AuthenticationService authenticationService;
private XPackLicenseState license;
private SecurityContext securityContext;
@Before
public void setupClient() {
@ -126,6 +128,8 @@ public class TransportCreateTokenActionTests extends ESTestCase {
return null;
}).when(client).execute(eq(IndexAction.INSTANCE), any(IndexRequest.class), any(ActionListener.class));
securityContext = new SecurityContext(Settings.EMPTY, threadPool.getThreadContext());
// setup lifecycle service
securityIndex = mock(SecurityIndexManager.class);
doAnswer(invocationOnMock -> {
@ -175,14 +179,14 @@ public class TransportCreateTokenActionTests extends ESTestCase {
}
public void testClientCredentialsCreatesWithoutRefreshToken() throws Exception {
final TokenService tokenService = new TokenService(SETTINGS, Clock.systemUTC(), client, license,
final TokenService tokenService = new TokenService(SETTINGS, Clock.systemUTC(), client, license, securityContext,
securityIndex, securityIndex, clusterService);
Authentication authentication = new Authentication(new User("joe"), new Authentication.RealmRef("realm", "type", "node"), null);
authentication.writeToContext(threadPool.getThreadContext());
final TransportCreateTokenAction action = new TransportCreateTokenAction(threadPool,
mock(TransportService.class), new ActionFilters(Collections.emptySet()), tokenService,
authenticationService);
authenticationService, securityContext);
final CreateTokenRequest createTokenRequest = new CreateTokenRequest();
createTokenRequest.setGrantType("client_credentials");
@ -200,14 +204,14 @@ public class TransportCreateTokenActionTests extends ESTestCase {
}
public void testPasswordGrantTypeCreatesWithRefreshToken() throws Exception {
final TokenService tokenService = new TokenService(SETTINGS, Clock.systemUTC(), client, license,
final TokenService tokenService = new TokenService(SETTINGS, Clock.systemUTC(), client, license, securityContext,
securityIndex, securityIndex, clusterService);
Authentication authentication = new Authentication(new User("joe"), new Authentication.RealmRef("realm", "type", "node"), null);
authentication.writeToContext(threadPool.getThreadContext());
final TransportCreateTokenAction action = new TransportCreateTokenAction(threadPool,
mock(TransportService.class), new ActionFilters(Collections.emptySet()), tokenService,
authenticationService);
authenticationService, securityContext);
final CreateTokenRequest createTokenRequest = new CreateTokenRequest();
createTokenRequest.setGrantType("password");
createTokenRequest.setUsername("user");
@ -227,14 +231,14 @@ public class TransportCreateTokenActionTests extends ESTestCase {
}
public void testKerberosGrantTypeCreatesWithRefreshToken() throws Exception {
final TokenService tokenService = new TokenService(SETTINGS, Clock.systemUTC(), client, license,
final TokenService tokenService = new TokenService(SETTINGS, Clock.systemUTC(), client, license, securityContext,
securityIndex, securityIndex, clusterService);
Authentication authentication = new Authentication(new User("joe"), new Authentication.RealmRef("realm", "type", "node"), null);
authentication.writeToContext(threadPool.getThreadContext());
final TransportCreateTokenAction action = new TransportCreateTokenAction(threadPool,
mock(TransportService.class), new ActionFilters(Collections.emptySet()), tokenService,
authenticationService);
authenticationService, securityContext);
final CreateTokenRequest createTokenRequest = new CreateTokenRequest();
createTokenRequest.setGrantType("_kerberos");
String failOrSuccess = randomBoolean() ? "fail" : "success";

View File

@ -16,10 +16,11 @@ import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.action.user.SetEnabledRequest;
import org.elasticsearch.xpack.core.security.action.user.SetEnabledResponse;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authc.AuthenticationField;
import org.elasticsearch.xpack.core.security.authc.support.AuthenticationContextSerializer;
import org.elasticsearch.xpack.core.security.user.AnonymousUser;
import org.elasticsearch.xpack.core.security.user.ElasticUser;
import org.elasticsearch.xpack.core.security.user.KibanaUser;
@ -53,20 +54,23 @@ import static org.mockito.Mockito.when;
*/
public class TransportSetEnabledActionTests extends ESTestCase {
public void testAnonymousUser() {
public void testAnonymousUser() throws Exception {
Settings settings = Settings.builder().put(AnonymousUser.ROLES_SETTING.getKey(), "superuser").build();
final User user = randomFrom(new ElasticUser(true), new KibanaUser(true), new User("joe"));
ThreadPool threadPool = mock(ThreadPool.class);
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
Authentication authentication = mock(Authentication.class);
when(threadPool.getThreadContext()).thenReturn(threadContext);
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, authentication);
Authentication authentication = mock(Authentication.class);
when(authentication.getUser()).thenReturn(user);
when(authentication.encode()).thenReturn(randomAlphaOfLength(24)); // just can't be null
new AuthenticationContextSerializer().writeToContext(authentication, threadContext);
NativeUsersStore usersStore = mock(NativeUsersStore.class);
TransportService transportService = new TransportService(Settings.EMPTY, mock(Transport.class), null,
TransportService.NOOP_TRANSPORT_INTERCEPTOR, x -> null, null, Collections.emptySet());
final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
TransportSetEnabledAction action = new TransportSetEnabledAction(settings, threadPool, transportService, mock(ActionFilters.class),
usersStore);
securityContext, usersStore);
SetEnabledRequest request = new SetEnabledRequest();
request.username(new AnonymousUser(settings).principal());
@ -92,19 +96,23 @@ public class TransportSetEnabledActionTests extends ESTestCase {
verifyZeroInteractions(usersStore);
}
public void testInternalUser() {
public void testInternalUser() throws Exception {
final User user = randomFrom(new ElasticUser(true), new KibanaUser(true), new User("joe"));
ThreadPool threadPool = mock(ThreadPool.class);
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
Authentication authentication = mock(Authentication.class);
when(threadPool.getThreadContext()).thenReturn(threadContext);
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, authentication);
Authentication authentication = mock(Authentication.class);
when(authentication.getUser()).thenReturn(user);
when(authentication.encode()).thenReturn(randomAlphaOfLength(24)); // just can't be null
new AuthenticationContextSerializer().writeToContext(authentication, threadContext);
NativeUsersStore usersStore = mock(NativeUsersStore.class);
TransportService transportService = new TransportService(Settings.EMPTY, mock(Transport.class), null,
TransportService.NOOP_TRANSPORT_INTERCEPTOR, x -> null, null, Collections.emptySet());
final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
TransportSetEnabledAction action = new TransportSetEnabledAction(Settings.EMPTY, threadPool, transportService,
mock(ActionFilters.class), usersStore);
mock(ActionFilters.class), securityContext, usersStore);
SetEnabledRequest request = new SetEnabledRequest();
request.username(randomFrom(SystemUser.INSTANCE.principal(), XPackUser.INSTANCE.principal()));
@ -130,13 +138,15 @@ public class TransportSetEnabledActionTests extends ESTestCase {
verifyZeroInteractions(usersStore);
}
public void testValidUser() {
public void testValidUser() throws Exception {
ThreadPool threadPool = mock(ThreadPool.class);
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
Authentication authentication = mock(Authentication.class);
when(threadPool.getThreadContext()).thenReturn(threadContext);
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, authentication);
Authentication authentication = mock(Authentication.class);
when(authentication.getUser()).thenReturn(new User("the runner"));
when(authentication.encode()).thenReturn(randomAlphaOfLength(24)); // just can't be null
new AuthenticationContextSerializer().writeToContext(authentication, threadContext);
final User user = randomFrom(new ElasticUser(true), new KibanaUser(true), new User("joe"));
NativeUsersStore usersStore = mock(NativeUsersStore.class);
@ -157,8 +167,9 @@ public class TransportSetEnabledActionTests extends ESTestCase {
.setEnabled(eq(user.principal()), eq(request.enabled()), eq(request.getRefreshPolicy()), any(ActionListener.class));
TransportService transportService = new TransportService(Settings.EMPTY, mock(Transport.class), null,
TransportService.NOOP_TRANSPORT_INTERCEPTOR, x -> null, null, Collections.emptySet());
final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
TransportSetEnabledAction action = new TransportSetEnabledAction(Settings.EMPTY, threadPool, transportService,
mock(ActionFilters.class), usersStore);
mock(ActionFilters.class), securityContext, usersStore);
final AtomicReference<Throwable> throwableRef = new AtomicReference<>();
final AtomicReference<SetEnabledResponse> responseRef = new AtomicReference<>();
@ -181,13 +192,15 @@ public class TransportSetEnabledActionTests extends ESTestCase {
.setEnabled(eq(user.principal()), eq(request.enabled()), eq(request.getRefreshPolicy()), any(ActionListener.class));
}
public void testException() {
public void testException() throws Exception {
ThreadPool threadPool = mock(ThreadPool.class);
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
Authentication authentication = mock(Authentication.class);
when(threadPool.getThreadContext()).thenReturn(threadContext);
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, authentication);
Authentication authentication = mock(Authentication.class);
when(authentication.getUser()).thenReturn(new User("the runner"));
when(authentication.encode()).thenReturn(randomAlphaOfLength(24)); // just can't be null
new AuthenticationContextSerializer().writeToContext(authentication, threadContext);
final User user = randomFrom(new ElasticUser(true), new KibanaUser(true), new User("joe"));
NativeUsersStore usersStore = mock(NativeUsersStore.class);
@ -209,8 +222,9 @@ public class TransportSetEnabledActionTests extends ESTestCase {
.setEnabled(eq(user.principal()), eq(request.enabled()), eq(request.getRefreshPolicy()), any(ActionListener.class));
TransportService transportService = new TransportService(Settings.EMPTY, mock(Transport.class), null,
TransportService.NOOP_TRANSPORT_INTERCEPTOR, x -> null, null, Collections.emptySet());
final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
TransportSetEnabledAction action = new TransportSetEnabledAction(Settings.EMPTY, threadPool, transportService,
mock(ActionFilters.class), usersStore);
mock(ActionFilters.class), securityContext, usersStore);
final AtomicReference<Throwable> throwableRef = new AtomicReference<>();
final AtomicReference<SetEnabledResponse> responseRef = new AtomicReference<>();
@ -233,14 +247,16 @@ public class TransportSetEnabledActionTests extends ESTestCase {
.setEnabled(eq(user.principal()), eq(request.enabled()), eq(request.getRefreshPolicy()), any(ActionListener.class));
}
public void testUserModifyingThemselves() {
public void testUserModifyingThemselves() throws Exception {
final User user = randomFrom(new ElasticUser(true), new KibanaUser(true), new User("joe"));
ThreadPool threadPool = mock(ThreadPool.class);
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
Authentication authentication = mock(Authentication.class);
when(threadPool.getThreadContext()).thenReturn(threadContext);
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, authentication);
Authentication authentication = mock(Authentication.class);
when(authentication.getUser()).thenReturn(user);
when(authentication.encode()).thenReturn(randomAlphaOfLength(24)); // just can't be null
new AuthenticationContextSerializer().writeToContext(authentication, threadContext);
NativeUsersStore usersStore = mock(NativeUsersStore.class);
SetEnabledRequest request = new SetEnabledRequest();
@ -249,8 +265,9 @@ public class TransportSetEnabledActionTests extends ESTestCase {
request.setRefreshPolicy(randomFrom(RefreshPolicy.values()));
TransportService transportService = new TransportService(Settings.EMPTY, mock(Transport.class), null,
TransportService.NOOP_TRANSPORT_INTERCEPTOR, x -> null, null, Collections.emptySet());
final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
TransportSetEnabledAction action = new TransportSetEnabledAction(Settings.EMPTY, threadPool, transportService,
mock(ActionFilters.class), usersStore);
mock(ActionFilters.class), securityContext, usersStore);
final AtomicReference<Throwable> throwableRef = new AtomicReference<>();
final AtomicReference<SetEnabledResponse> responseRef = new AtomicReference<>();

View File

@ -57,6 +57,7 @@ import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportMessage;
import org.elasticsearch.xpack.core.XPackField;
import org.elasticsearch.xpack.core.XPackSettings;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType;
import org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef;
@ -231,8 +232,10 @@ public class AuthenticationServiceTests extends ESTestCase {
return null;
}).when(securityIndex).checkIndexVersionThenExecute(any(Consumer.class), any(Runnable.class));
ClusterService clusterService = ClusterServiceUtils.createClusterService(threadPool);
final SecurityContext securityContext = new SecurityContext(settings, threadContext);
apiKeyService = new ApiKeyService(settings, Clock.systemUTC(), client, licenseState, securityIndex, clusterService, threadPool);
tokenService = new TokenService(settings, Clock.systemUTC(), client, licenseState, securityIndex, securityIndex, clusterService);
tokenService = new TokenService(settings, Clock.systemUTC(), client, licenseState, securityContext, securityIndex, securityIndex,
clusterService);
service = new AuthenticationService(settings, realms, auditTrail, new DefaultAuthenticationFailureHandler(Collections.emptyMap()),
threadPool, new AnonymousUser(settings), tokenService, apiKeyService);
}

View File

@ -46,6 +46,7 @@ import org.elasticsearch.test.EqualsHashCodeTestUtils;
import org.elasticsearch.threadpool.FixedExecutorBuilder;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.core.XPackSettings;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType;
import org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef;
@ -103,6 +104,7 @@ public class TokenServiceTests extends ESTestCase {
private Settings tokenServiceEnabledSettings = Settings.builder()
.put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), true).build();
private XPackLicenseState licenseState;
private SecurityContext securityContext;
@Before
public void setupClient() {
@ -127,6 +129,7 @@ public class TokenServiceTests extends ESTestCase {
return null;
}).when(client).execute(eq(IndexAction.INSTANCE), any(IndexRequest.class), any(ActionListener.class));
this.securityContext = new SecurityContext(settings, threadPool.getThreadContext());
// setup lifecycle service
this.securityMainIndex = SecurityMocks.mockSecurityIndexManager();
this.securityTokensIndex = SecurityMocks.mockSecurityIndexManager();
@ -557,7 +560,7 @@ public class TokenServiceTests extends ESTestCase {
TokenService tokenService = new TokenService(Settings.builder()
.put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), false)
.build(),
Clock.systemUTC(), client, licenseState, securityMainIndex, securityTokensIndex, clusterService);
Clock.systemUTC(), client, licenseState, securityContext, securityMainIndex, securityTokensIndex, clusterService);
IllegalStateException e = expectThrows(IllegalStateException.class,
() -> tokenService.createOAuth2Tokens(null, null, null, true, null));
assertEquals("security tokens are not enabled", e.getMessage());
@ -763,7 +766,8 @@ public class TokenServiceTests extends ESTestCase {
}
private TokenService createTokenService(Settings settings, Clock clock) throws GeneralSecurityException {
return new TokenService(settings, clock, client, licenseState, securityMainIndex, securityTokensIndex, clusterService);
return new TokenService(settings, clock, client, licenseState, securityContext, securityMainIndex, securityTokensIndex,
clusterService);
}
private void mockGetTokenFromId(TokenService tokenService, String accessToken, Authentication authentication, boolean isExpired) {

View File

@ -19,6 +19,7 @@ import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.TestSearchContext;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.transport.TransportRequest.Empty;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authc.Authentication.RealmRef;
import org.elasticsearch.xpack.core.security.authc.AuthenticationField;
@ -47,11 +48,12 @@ public class SecuritySearchOperationListenerTests extends ESTestCase {
XPackLicenseState licenseState = mock(XPackLicenseState.class);
when(licenseState.isAuthAllowed()).thenReturn(false);
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
AuditTrailService auditTrailService = mock(AuditTrailService.class);
SearchContext searchContext = mock(SearchContext.class);
when(searchContext.scrollContext()).thenReturn(new ScrollContext());
SecuritySearchOperationListener listener = new SecuritySearchOperationListener(threadContext, licenseState, auditTrailService);
SecuritySearchOperationListener listener = new SecuritySearchOperationListener(securityContext, licenseState, auditTrailService);
listener.onNewScrollContext(searchContext);
listener.validateSearchContext(searchContext, Empty.INSTANCE);
verify(licenseState, times(2)).isAuthAllowed();
@ -66,11 +68,12 @@ public class SecuritySearchOperationListenerTests extends ESTestCase {
XPackLicenseState licenseState = mock(XPackLicenseState.class);
when(licenseState.isAuthAllowed()).thenReturn(true);
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
AuditTrailService auditTrailService = mock(AuditTrailService.class);
Authentication authentication = new Authentication(new User("test", "role"), new RealmRef("realm", "file", "node"), null);
authentication.writeToContext(threadContext);
SecuritySearchOperationListener listener = new SecuritySearchOperationListener(threadContext, licenseState, auditTrailService);
SecuritySearchOperationListener listener = new SecuritySearchOperationListener(securityContext, licenseState, auditTrailService);
listener.onNewScrollContext(testSearchContext);
Authentication contextAuth = testSearchContext.scrollContext().getFromContext(AuthenticationField.AUTHENTICATION_KEY);
@ -90,9 +93,10 @@ public class SecuritySearchOperationListenerTests extends ESTestCase {
XPackLicenseState licenseState = mock(XPackLicenseState.class);
when(licenseState.isAuthAllowed()).thenReturn(true);
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
final SecurityContext securityContext = new SecurityContext(Settings.EMPTY, threadContext);
AuditTrailService auditTrailService = mock(AuditTrailService.class);
SecuritySearchOperationListener listener = new SecuritySearchOperationListener(threadContext, licenseState, auditTrailService);
SecuritySearchOperationListener listener = new SecuritySearchOperationListener(securityContext, licenseState, auditTrailService);
try (StoredContext ignore = threadContext.newStoredContext(false)) {
Authentication authentication = new Authentication(new User("test", "role"), new RealmRef("realm", "file", "node"), null);
authentication.writeToContext(threadContext);

View File

@ -6,20 +6,33 @@
package org.elasticsearch.xpack.security.ingest;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.security.ingest.SetSecurityUserProcessor.Property;
import org.junit.Before;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Map;
import static org.elasticsearch.test.TestMatchers.throwableWithMessage;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
public class SetSecurityUserProcessorFactoryTests extends ESTestCase {
private SecurityContext securityContext;
@Before
public void setupContext() {
securityContext = new SecurityContext(Settings.EMPTY, new ThreadContext(Settings.EMPTY));
}
public void testProcessor() throws Exception {
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(null);
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(() -> securityContext);
Map<String, Object> config = new HashMap<>();
config.put("field", "_field");
SetSecurityUserProcessor processor = factory.create(null, "_tag", config);
@ -28,7 +41,7 @@ public class SetSecurityUserProcessorFactoryTests extends ESTestCase {
}
public void testProcessor_noField() throws Exception {
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(null);
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(() -> securityContext);
Map<String, Object> config = new HashMap<>();
ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class, () -> factory.create(null, "_tag", config));
assertThat(e.getMetadata("es.property_name").get(0), equalTo("field"));
@ -37,7 +50,7 @@ public class SetSecurityUserProcessorFactoryTests extends ESTestCase {
}
public void testProcessor_validProperties() throws Exception {
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(null);
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(() -> securityContext);
Map<String, Object> config = new HashMap<>();
config.put("field", "_field");
config.put("properties", Arrays.asList(Property.USERNAME.name(), Property.ROLES.name()));
@ -47,7 +60,7 @@ public class SetSecurityUserProcessorFactoryTests extends ESTestCase {
}
public void testProcessor_invalidProperties() throws Exception {
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(null);
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(() -> securityContext);
Map<String, Object> config = new HashMap<>();
config.put("field", "_field");
config.put("properties", Arrays.asList("invalid"));
@ -57,4 +70,12 @@ public class SetSecurityUserProcessorFactoryTests extends ESTestCase {
assertThat(e.getMetadata("es.processor_tag").get(0), equalTo("_tag"));
}
public void testNullSecurityContextThrowsException() throws Exception {
SetSecurityUserProcessor.Factory factory = new SetSecurityUserProcessor.Factory(() -> null);
Map<String, Object> config = new HashMap<>();
config.put("field", "_field");
NullPointerException e = expectThrows(NullPointerException.class, () -> factory.create(null, "_tag", config));
assertThat(e, throwableWithMessage(containsString("security context")));
}
}

View File

@ -9,10 +9,13 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.ingest.IngestDocument;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authc.AuthenticationField;
import org.elasticsearch.xpack.core.security.authc.Authentication.AuthenticationType;
import org.elasticsearch.xpack.core.security.authc.support.AuthenticationContextSerializer;
import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.security.ingest.SetSecurityUserProcessor.Property;
import org.junit.Before;
import org.mockito.Mockito;
import java.util.Collections;
@ -25,15 +28,23 @@ import static org.hamcrest.Matchers.equalTo;
public class SetSecurityUserProcessorTests extends ESTestCase {
public void testProcessor() throws Exception {
User user = new User("_username", new String[]{"role1", "role2"}, "firstname lastname", "_email",
Collections.singletonMap("key", "value"), true);
private ThreadContext threadContext;
private SecurityContext securityContext;
@Before
public void setupObjects() {
threadContext = new ThreadContext(Settings.EMPTY);
securityContext = new SecurityContext(Settings.EMPTY, threadContext);
}
public void testProcessorWithData() throws Exception {
User user = new User("_username", new String[] { "role1", "role2" }, "firstname lastname", "_email",
Collections.singletonMap("key", "value"), true);
Authentication.RealmRef realmRef = new Authentication.RealmRef("_name", "_type", "_node_name");
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, new Authentication(user, realmRef, null));
new Authentication(user, realmRef, null).writeToContext(threadContext);
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", threadContext, "_field", EnumSet.allOf(Property.class));
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.allOf(Property.class));
processor.execute(ingestDocument);
Map<String, Object> result = ingestDocument.getFieldValue("_field", Map.class);
@ -51,33 +62,32 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
User user = Mockito.mock(User.class);
Authentication authentication = Mockito.mock(Authentication.class);
Mockito.when(authentication.getUser()).thenReturn(user);
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, authentication);
Mockito.when(authentication.getAuthenticatedBy()).thenReturn(new Authentication.RealmRef("_name", "_type", "_node_name"));
Mockito.when(authentication.getAuthenticationType()).thenReturn(AuthenticationType.REALM);
Mockito.when(authentication.encode()).thenReturn(randomAlphaOfLength(24)); // don't care as long as it's not null
new AuthenticationContextSerializer().writeToContext(authentication, threadContext);
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", threadContext, "_field", EnumSet.allOf(Property.class));
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.allOf(Property.class));
processor.execute(ingestDocument);
Map result = ingestDocument.getFieldValue("_field", Map.class);
assertThat(result.size(), equalTo(0));
}
public void testNoCurrentUser() throws Exception {
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", threadContext, "_field", EnumSet.allOf(Property.class));
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.allOf(Property.class));
IllegalStateException e = expectThrows(IllegalStateException.class, () -> processor.execute(ingestDocument));
assertThat(e.getMessage(), equalTo("No user authenticated, only use this processor via authenticated user"));
}
public void testUsernameProperties() throws Exception {
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
User user = new User("_username", null, null);
Authentication.RealmRef realmRef = new Authentication.RealmRef("_name", "_type", "_node_name");
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, new Authentication(user, realmRef, null));
new Authentication(user, realmRef, null).writeToContext(threadContext);
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", threadContext, "_field", EnumSet.of(Property.USERNAME));
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.of(Property.USERNAME));
processor.execute(ingestDocument);
@SuppressWarnings("unchecked")
@ -87,13 +97,12 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
}
public void testRolesProperties() throws Exception {
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
User user = new User(randomAlphaOfLengthBetween(4, 12), "role1", "role2");
Authentication.RealmRef realmRef = new Authentication.RealmRef("_name", "_type", "_node_name");
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, new Authentication(user, realmRef, null));
new Authentication(user, realmRef, null).writeToContext(threadContext);
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", threadContext, "_field", EnumSet.of(Property.ROLES));
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.of(Property.ROLES));
processor.execute(ingestDocument);
@SuppressWarnings("unchecked")
@ -103,13 +112,13 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
}
public void testFullNameProperties() throws Exception {
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
User user = new User(randomAlphaOfLengthBetween(4, 12), null, "_full_name", null, null, true);
Authentication.RealmRef realmRef = new Authentication.RealmRef("_name", "_type", "_node_name");
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, new Authentication(user, realmRef, null));
new Authentication(user, realmRef, null).writeToContext(threadContext);
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", threadContext, "_field", EnumSet.of(Property.FULL_NAME));
SetSecurityUserProcessor processor
= new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.of(Property.FULL_NAME));
processor.execute(ingestDocument);
@SuppressWarnings("unchecked")
@ -119,13 +128,12 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
}
public void testEmailProperties() throws Exception {
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
User user = new User(randomAlphaOfLengthBetween(4, 12), null, null, "_email", null, true);
Authentication.RealmRef realmRef = new Authentication.RealmRef("_name", "_type", "_node_name");
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, new Authentication(user, realmRef, null));
new Authentication(user, realmRef, null).writeToContext(threadContext);
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", threadContext, "_field", EnumSet.of(Property.EMAIL));
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.of(Property.EMAIL));
processor.execute(ingestDocument);
@SuppressWarnings("unchecked")
@ -135,13 +143,12 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
}
public void testMetadataProperties() throws Exception {
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
User user = new User(randomAlphaOfLengthBetween(4, 12), null, null, null, Collections.singletonMap("key", "value"), true);
Authentication.RealmRef realmRef = new Authentication.RealmRef("_name", "_type", "_node_name");
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, new Authentication(user, realmRef, null));
new Authentication(user, realmRef, null).writeToContext(threadContext);
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", threadContext, "_field", EnumSet.of(Property.METADATA));
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.of(Property.METADATA));
processor.execute(ingestDocument);
@SuppressWarnings("unchecked")
@ -152,12 +159,11 @@ public class SetSecurityUserProcessorTests extends ESTestCase {
}
public void testOverwriteExistingField() throws Exception {
ThreadContext threadContext = new ThreadContext(Settings.EMPTY);
User user = new User("_username", null, null);
Authentication.RealmRef realmRef = new Authentication.RealmRef("_name", "_type", "_node_name");
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, new Authentication(user, realmRef, null));
new Authentication(user, realmRef, null).writeToContext(threadContext);
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", threadContext, "_field", EnumSet.of(Property.USERNAME));
SetSecurityUserProcessor processor = new SetSecurityUserProcessor("_tag", securityContext, "_field", EnumSet.of(Property.USERNAME));
IngestDocument ingestDocument = new IngestDocument(new HashMap<>(), new HashMap<>());
ingestDocument.setFieldValue("_field", "test");