Support authentication without anonymous user (#53528)
This change adds a new parameter to the authenticate methods in the AuthenticationService to optionally exclude support for the anonymous user (if an anonymous user exists). Backport of: #52094
This commit is contained in:
parent
9dfcc07401
commit
bac1740d44
|
@ -118,11 +118,27 @@ public class AuthenticationService {
|
||||||
* Authenticates the user that is associated with the given request. If the user was authenticated successfully (i.e.
|
* Authenticates the user that is associated with the given request. If the user was authenticated successfully (i.e.
|
||||||
* a user was indeed associated with the request and the credentials were verified to be valid), the method returns
|
* a user was indeed associated with the request and the credentials were verified to be valid), the method returns
|
||||||
* the user and that user is then "attached" to the request's context.
|
* the user and that user is then "attached" to the request's context.
|
||||||
|
* This method will authenticate as the anonymous user if the service is configured to allow anonymous access.
|
||||||
*
|
*
|
||||||
* @param request The request to be authenticated
|
* @param request The request to be authenticated
|
||||||
*/
|
*/
|
||||||
public void authenticate(RestRequest request, ActionListener<Authentication> authenticationListener) {
|
public void authenticate(RestRequest request, ActionListener<Authentication> authenticationListener) {
|
||||||
createAuthenticator(request, authenticationListener).authenticateAsync();
|
authenticate(request, true, authenticationListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticates the user that is associated with the given request. If the user was authenticated successfully (i.e.
|
||||||
|
* a user was indeed associated with the request and the credentials were verified to be valid), the method returns
|
||||||
|
* the user and that user is then "attached" to the request's context.
|
||||||
|
* This method will optionally, authenticate as the anonymous user if the service is configured to allow anonymous access.
|
||||||
|
*
|
||||||
|
* @param request The request to be authenticated
|
||||||
|
* @param allowAnonymous If {@code false}, then authentication will <em>not</em> fallback to anonymous.
|
||||||
|
* If {@code true}, then authentication <em>will</em> fallback to anonymous, if this service is
|
||||||
|
* configured to allow anonymous access (see {@link #isAnonymousUserEnabled}).
|
||||||
|
*/
|
||||||
|
public void authenticate(RestRequest request, boolean allowAnonymous, ActionListener<Authentication> authenticationListener) {
|
||||||
|
createAuthenticator(request, allowAnonymous, authenticationListener).authenticateAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -133,15 +149,31 @@ public class AuthenticationService {
|
||||||
*
|
*
|
||||||
* @param action The action of the message
|
* @param action The action of the message
|
||||||
* @param message The message to be authenticated
|
* @param message The message to be authenticated
|
||||||
* @param fallbackUser The default user that will be assumed if no other user is attached to the message. Can be
|
* @param fallbackUser The default user that will be assumed if no other user is attached to the message. May not
|
||||||
* {@code null}, in which case there will be no fallback user and the success/failure of the
|
* be {@code null}.
|
||||||
* authentication will be based on the whether there's an attached user to in the message and
|
|
||||||
* if there is, whether its credentials are valid.
|
|
||||||
*/
|
*/
|
||||||
public void authenticate(String action, TransportMessage message, User fallbackUser, ActionListener<Authentication> listener) {
|
public void authenticate(String action, TransportMessage message, User fallbackUser, ActionListener<Authentication> listener) {
|
||||||
|
Objects.requireNonNull(fallbackUser, "fallback user may not be null");
|
||||||
createAuthenticator(action, message, fallbackUser, listener).authenticateAsync();
|
createAuthenticator(action, message, fallbackUser, listener).authenticateAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authenticates the user that is associated with the given message. If the user was authenticated successfully (i.e.
|
||||||
|
* a user was indeed associated with the request and the credentials were verified to be valid), the method returns
|
||||||
|
* the user and that user is then "attached" to the message's context.
|
||||||
|
* If no user or credentials are found to be attached to the given message, and the caller allows anonymous access
|
||||||
|
* ({@code allowAnonymous} parameter), and this service is configured for anonymous access (see {@link #isAnonymousUserEnabled} and
|
||||||
|
* {@link #anonymousUser}), then the anonymous user will be returned instead.
|
||||||
|
*
|
||||||
|
* @param action The action of the message
|
||||||
|
* @param message The message to be authenticated
|
||||||
|
* @param allowAnonymous Whether to permit anonymous access for this request (this only relevant if the service is
|
||||||
|
* {@link #isAnonymousUserEnabled configured for anonymous access}).
|
||||||
|
*/
|
||||||
|
public void authenticate(String action, TransportMessage message, boolean allowAnonymous, ActionListener<Authentication> listener) {
|
||||||
|
createAuthenticator(action, message, allowAnonymous, listener).authenticateAsync();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Authenticates the user based on the contents of the token that is provided as parameter. This will not look at the values in the
|
* Authenticates the user based on the contents of the token that is provided as parameter. This will not look at the values in the
|
||||||
* ThreadContext for Authentication.
|
* ThreadContext for Authentication.
|
||||||
|
@ -152,7 +184,7 @@ public class AuthenticationService {
|
||||||
*/
|
*/
|
||||||
public void authenticate(String action, TransportMessage message,
|
public void authenticate(String action, TransportMessage message,
|
||||||
AuthenticationToken token, ActionListener<Authentication> listener) {
|
AuthenticationToken token, ActionListener<Authentication> listener) {
|
||||||
new Authenticator(action, message, null, listener).authenticateToken(token);
|
new Authenticator(action, message, shouldFallbackToAnonymous(true), listener).authenticateToken(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void expire(String principal) {
|
public void expire(String principal) {
|
||||||
|
@ -178,12 +210,19 @@ public class AuthenticationService {
|
||||||
}
|
}
|
||||||
|
|
||||||
// pkg private method for testing
|
// pkg private method for testing
|
||||||
Authenticator createAuthenticator(RestRequest request, ActionListener<Authentication> listener) {
|
Authenticator createAuthenticator(RestRequest request, boolean fallbackToAnonymous, ActionListener<Authentication> listener) {
|
||||||
return new Authenticator(request, listener);
|
return new Authenticator(request, shouldFallbackToAnonymous(fallbackToAnonymous), listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
// pkg private method for testing
|
// pkg private method for testing
|
||||||
Authenticator createAuthenticator(String action, TransportMessage message, User fallbackUser, ActionListener<Authentication> listener) {
|
Authenticator createAuthenticator(String action, TransportMessage message, boolean fallbackToAnonymous,
|
||||||
|
ActionListener<Authentication> listener) {
|
||||||
|
return new Authenticator(action, message, shouldFallbackToAnonymous(fallbackToAnonymous), listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
// pkg private method for testing
|
||||||
|
Authenticator createAuthenticator(String action, TransportMessage message, User fallbackUser,
|
||||||
|
ActionListener<Authentication> listener) {
|
||||||
return new Authenticator(action, message, fallbackUser, listener);
|
return new Authenticator(action, message, fallbackUser, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -192,6 +231,31 @@ public class AuthenticationService {
|
||||||
return numInvalidation.get();
|
return numInvalidation.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines whether to support anonymous access for the current request. Returns {@code true} if all of the following are true
|
||||||
|
* <ul>
|
||||||
|
* <li>The service has anonymous authentication enabled (see {@link #isAnonymousUserEnabled})</li>
|
||||||
|
* <li>Anonymous access is accepted for this request ({@code allowAnonymousOnThisRequest} parameter)
|
||||||
|
* <li>The {@link ThreadContext} does not provide API Key or Bearer Token credentials. If these are present, we
|
||||||
|
* treat the request as though it attempted to authenticate (even if that failed), and will not fall back to anonymous.</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
boolean shouldFallbackToAnonymous(boolean allowAnonymousOnThisRequest) {
|
||||||
|
if (isAnonymousUserEnabled == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (allowAnonymousOnThisRequest == false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String header = threadContext.getHeader("Authorization");
|
||||||
|
if (Strings.hasText(header) &&
|
||||||
|
((header.regionMatches(true, 0, "Bearer ", 0, "Bearer ".length()) && header.length() > "Bearer ".length()) ||
|
||||||
|
(header.regionMatches(true, 0, "ApiKey ", 0, "ApiKey ".length()) && header.length() > "ApiKey ".length()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is responsible for taking a request and executing the authentication. The authentication is executed in an asynchronous
|
* This class is responsible for taking a request and executing the authentication. The authentication is executed in an asynchronous
|
||||||
* fashion in order to avoid blocking calls on a network thread. This class also performs the auditing necessary around authentication
|
* fashion in order to avoid blocking calls on a network thread. This class also performs the auditing necessary around authentication
|
||||||
|
@ -200,6 +264,7 @@ public class AuthenticationService {
|
||||||
|
|
||||||
private final AuditableRequest request;
|
private final AuditableRequest request;
|
||||||
private final User fallbackUser;
|
private final User fallbackUser;
|
||||||
|
private final boolean fallbackToAnonymous;
|
||||||
private final List<Realm> defaultOrderedRealmList;
|
private final List<Realm> defaultOrderedRealmList;
|
||||||
private final ActionListener<Authentication> listener;
|
private final ActionListener<Authentication> listener;
|
||||||
|
|
||||||
|
@ -208,18 +273,25 @@ public class AuthenticationService {
|
||||||
private AuthenticationToken authenticationToken = null;
|
private AuthenticationToken authenticationToken = null;
|
||||||
private AuthenticationResult authenticationResult = null;
|
private AuthenticationResult authenticationResult = null;
|
||||||
|
|
||||||
Authenticator(RestRequest request, ActionListener<Authentication> listener) {
|
Authenticator(RestRequest request, boolean fallbackToAnonymous, ActionListener<Authentication> listener) {
|
||||||
this(new AuditableRestRequest(auditTrail, failureHandler, threadContext, request), null, listener);
|
this(new AuditableRestRequest(auditTrail, failureHandler, threadContext, request), null, fallbackToAnonymous, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
Authenticator(String action, TransportMessage message, boolean fallbackToAnonymous, ActionListener<Authentication> listener) {
|
||||||
|
this(new AuditableTransportRequest(auditTrail, failureHandler, threadContext, action, message),
|
||||||
|
null, fallbackToAnonymous, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
Authenticator(String action, TransportMessage message, User fallbackUser, ActionListener<Authentication> listener) {
|
Authenticator(String action, TransportMessage message, User fallbackUser, ActionListener<Authentication> listener) {
|
||||||
this(new AuditableTransportRequest(auditTrail, failureHandler, threadContext, action, message
|
this(new AuditableTransportRequest(auditTrail, failureHandler, threadContext, action, message),
|
||||||
), fallbackUser, listener);
|
Objects.requireNonNull(fallbackUser, "Fallback user cannot be null"), false, listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Authenticator(AuditableRequest auditableRequest, User fallbackUser, ActionListener<Authentication> listener) {
|
private Authenticator(AuditableRequest auditableRequest, User fallbackUser, boolean fallbackToAnonymous,
|
||||||
|
ActionListener<Authentication> listener) {
|
||||||
this.request = auditableRequest;
|
this.request = auditableRequest;
|
||||||
this.fallbackUser = fallbackUser;
|
this.fallbackUser = fallbackUser;
|
||||||
|
this.fallbackToAnonymous = fallbackToAnonymous;
|
||||||
this.defaultOrderedRealmList = realms.asList();
|
this.defaultOrderedRealmList = realms.asList();
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
@ -479,7 +551,7 @@ public class AuthenticationService {
|
||||||
RealmRef authenticatedBy = new RealmRef("__fallback", "__fallback", nodeName);
|
RealmRef authenticatedBy = new RealmRef("__fallback", "__fallback", nodeName);
|
||||||
authentication = new Authentication(fallbackUser, authenticatedBy, null, Version.CURRENT, AuthenticationType.INTERNAL,
|
authentication = new Authentication(fallbackUser, authenticatedBy, null, Version.CURRENT, AuthenticationType.INTERNAL,
|
||||||
Collections.emptyMap());
|
Collections.emptyMap());
|
||||||
} else if (isAnonymousUserEnabled && shouldFallbackToAnonymous()) {
|
} else if (fallbackToAnonymous) {
|
||||||
logger.trace("No valid credentials found in request [{}], using anonymous [{}]", request, anonymousUser.principal());
|
logger.trace("No valid credentials found in request [{}], using anonymous [{}]", request, anonymousUser.principal());
|
||||||
RealmRef authenticatedBy = new RealmRef("__anonymous", "__anonymous", nodeName);
|
RealmRef authenticatedBy = new RealmRef("__anonymous", "__anonymous", nodeName);
|
||||||
authentication = new Authentication(anonymousUser, authenticatedBy, null, Version.CURRENT, AuthenticationType.ANONYMOUS,
|
authentication = new Authentication(anonymousUser, authenticatedBy, null, Version.CURRENT, AuthenticationType.ANONYMOUS,
|
||||||
|
@ -503,20 +575,6 @@ public class AuthenticationService {
|
||||||
action.run();
|
action.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* When an API Key or an Elasticsearch Token Service token is used for authentication and authentication fails (as indicated by
|
|
||||||
* a null AuthenticationToken) we should not fallback to the anonymous user.
|
|
||||||
*/
|
|
||||||
boolean shouldFallbackToAnonymous(){
|
|
||||||
String header = threadContext.getHeader("Authorization");
|
|
||||||
if (Strings.hasText(header) &&
|
|
||||||
((header.regionMatches(true, 0, "Bearer ", 0, "Bearer ".length()) && header.length() > "Bearer ".length()) ||
|
|
||||||
(header.regionMatches(true, 0, "ApiKey ", 0, "ApiKey ".length()) && header.length() > "ApiKey ".length()))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Consumes the {@link User} that resulted from attempting to authenticate a token against the {@link Realms}. When the user is
|
* Consumes the {@link User} that resulted from attempting to authenticate a token against the {@link Realms}. When the user is
|
||||||
* {@code null}, authentication fails and does not proceed. When there is a user, the request is inspected to see if the run as
|
* {@code null}, authentication fails and does not proceed. When there is a user, the request is inspected to see if the run as
|
||||||
|
|
|
@ -27,7 +27,6 @@ import org.elasticsearch.transport.nio.NioTcpChannel;
|
||||||
import org.elasticsearch.xpack.core.security.SecurityContext;
|
import org.elasticsearch.xpack.core.security.SecurityContext;
|
||||||
import org.elasticsearch.xpack.core.security.authc.Authentication;
|
import org.elasticsearch.xpack.core.security.authc.Authentication;
|
||||||
import org.elasticsearch.xpack.core.security.user.SystemUser;
|
import org.elasticsearch.xpack.core.security.user.SystemUser;
|
||||||
import org.elasticsearch.xpack.core.security.user.User;
|
|
||||||
import org.elasticsearch.xpack.security.action.SecurityActionMapper;
|
import org.elasticsearch.xpack.security.action.SecurityActionMapper;
|
||||||
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
import org.elasticsearch.xpack.security.authc.AuthenticationService;
|
||||||
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
import org.elasticsearch.xpack.security.authz.AuthorizationService;
|
||||||
|
@ -118,7 +117,7 @@ public interface ServerTransportFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
final Version version = transportChannel.getVersion();
|
final Version version = transportChannel.getVersion();
|
||||||
authcService.authenticate(securityAction, request, (User)null, ActionListener.wrap((authentication) -> {
|
authcService.authenticate(securityAction, request, true, ActionListener.wrap((authentication) -> {
|
||||||
if (authentication != null) {
|
if (authentication != null) {
|
||||||
if (securityAction.equals(TransportService.HANDSHAKE_ACTION_NAME) &&
|
if (securityAction.equals(TransportService.HANDSHAKE_ACTION_NAME) &&
|
||||||
SystemUser.is(authentication.getUser()) == false) {
|
SystemUser.is(authentication.getUser()) == false) {
|
||||||
|
|
|
@ -40,6 +40,7 @@ import org.junit.Before;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.arrayWithSize;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.eq;
|
import static org.mockito.Matchers.eq;
|
||||||
import static org.mockito.Matchers.isA;
|
import static org.mockito.Matchers.isA;
|
||||||
|
@ -92,14 +93,17 @@ public class SecurityActionFilterTests extends ESTestCase {
|
||||||
Task task = mock(Task.class);
|
Task task = mock(Task.class);
|
||||||
User user = new User("username", "r1", "r2");
|
User user = new User("username", "r1", "r2");
|
||||||
Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null);
|
Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null);
|
||||||
doAnswer((i) -> {
|
doAnswer(i -> {
|
||||||
ActionListener callback =
|
final Object[] args = i.getArguments();
|
||||||
(ActionListener) i.getArguments()[3];
|
assertThat(args, arrayWithSize(4));
|
||||||
|
ActionListener callback = (ActionListener) args[args.length - 1];
|
||||||
callback.onResponse(authentication);
|
callback.onResponse(authentication);
|
||||||
return Void.TYPE;
|
return Void.TYPE;
|
||||||
}).when(authcService).authenticate(eq("_action"), eq(request), eq(SystemUser.INSTANCE), any(ActionListener.class));
|
}).when(authcService).authenticate(eq("_action"), eq(request), eq(SystemUser.INSTANCE), any(ActionListener.class));
|
||||||
doAnswer((i) -> {
|
doAnswer(i -> {
|
||||||
ActionListener<Void> callback = (ActionListener<Void>) i.getArguments()[3];
|
final Object[] args = i.getArguments();
|
||||||
|
assertThat(args, arrayWithSize(4));
|
||||||
|
ActionListener callback = (ActionListener) args[args.length - 1];
|
||||||
callback.onResponse(null);
|
callback.onResponse(null);
|
||||||
return Void.TYPE;
|
return Void.TYPE;
|
||||||
}).when(authzService)
|
}).when(authzService)
|
||||||
|
@ -116,16 +120,19 @@ public class SecurityActionFilterTests extends ESTestCase {
|
||||||
Task task = mock(Task.class);
|
Task task = mock(Task.class);
|
||||||
User user = new User("username", "r1", "r2");
|
User user = new User("username", "r1", "r2");
|
||||||
Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null);
|
Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null);
|
||||||
doAnswer((i) -> {
|
doAnswer(i -> {
|
||||||
ActionListener callback =
|
final Object[] args = i.getArguments();
|
||||||
(ActionListener) i.getArguments()[3];
|
assertThat(args, arrayWithSize(4));
|
||||||
|
ActionListener callback = (ActionListener) args[args.length - 1];
|
||||||
assertNull(threadContext.getTransient(AuthenticationField.AUTHENTICATION_KEY));
|
assertNull(threadContext.getTransient(AuthenticationField.AUTHENTICATION_KEY));
|
||||||
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, authentication);
|
threadContext.putTransient(AuthenticationField.AUTHENTICATION_KEY, authentication);
|
||||||
callback.onResponse(authentication);
|
callback.onResponse(authentication);
|
||||||
return Void.TYPE;
|
return Void.TYPE;
|
||||||
}).when(authcService).authenticate(eq("_action"), eq(request), eq(SystemUser.INSTANCE), any(ActionListener.class));
|
}).when(authcService).authenticate(eq("_action"), eq(request), eq(SystemUser.INSTANCE), any(ActionListener.class));
|
||||||
doAnswer((i) -> {
|
doAnswer(i -> {
|
||||||
ActionListener<Void> callback = (ActionListener<Void>) i.getArguments()[3];
|
final Object[] args = i.getArguments();
|
||||||
|
assertThat(args, arrayWithSize(4));
|
||||||
|
ActionListener callback = (ActionListener) args[args.length - 1];
|
||||||
callback.onResponse(null);
|
callback.onResponse(null);
|
||||||
return Void.TYPE;
|
return Void.TYPE;
|
||||||
}).when(authzService)
|
}).when(authzService)
|
||||||
|
@ -158,9 +165,10 @@ public class SecurityActionFilterTests extends ESTestCase {
|
||||||
} else {
|
} else {
|
||||||
assertNull(threadContext.getTransient(AuthenticationField.AUTHENTICATION_KEY));
|
assertNull(threadContext.getTransient(AuthenticationField.AUTHENTICATION_KEY));
|
||||||
}
|
}
|
||||||
doAnswer((i) -> {
|
doAnswer(i -> {
|
||||||
ActionListener callback =
|
final Object[] args = i.getArguments();
|
||||||
(ActionListener) i.getArguments()[3];
|
assertThat(args, arrayWithSize(4));
|
||||||
|
ActionListener callback = (ActionListener) args[args.length - 1];
|
||||||
callback.onResponse(threadContext.getTransient(AuthenticationField.AUTHENTICATION_KEY));
|
callback.onResponse(threadContext.getTransient(AuthenticationField.AUTHENTICATION_KEY));
|
||||||
return Void.TYPE;
|
return Void.TYPE;
|
||||||
}).when(authcService).authenticate(eq(action), eq(request), eq(SystemUser.INSTANCE), any(ActionListener.class));
|
}).when(authcService).authenticate(eq(action), eq(request), eq(SystemUser.INSTANCE), any(ActionListener.class));
|
||||||
|
@ -193,9 +201,10 @@ public class SecurityActionFilterTests extends ESTestCase {
|
||||||
Task task = mock(Task.class);
|
Task task = mock(Task.class);
|
||||||
User user = new User("username", "r1", "r2");
|
User user = new User("username", "r1", "r2");
|
||||||
Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null);
|
Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null);
|
||||||
doAnswer((i) -> {
|
doAnswer(i -> {
|
||||||
ActionListener callback =
|
final Object[] args = i.getArguments();
|
||||||
(ActionListener) i.getArguments()[3];
|
assertThat(args, arrayWithSize(4));
|
||||||
|
ActionListener callback = (ActionListener) args[args.length - 1];
|
||||||
callback.onResponse(authentication);
|
callback.onResponse(authentication);
|
||||||
return Void.TYPE;
|
return Void.TYPE;
|
||||||
}).when(authcService).authenticate(eq(action), eq(request), eq(SystemUser.INSTANCE), any(ActionListener.class));
|
}).when(authcService).authenticate(eq(action), eq(request), eq(SystemUser.INSTANCE), any(ActionListener.class));
|
||||||
|
@ -223,9 +232,10 @@ public class SecurityActionFilterTests extends ESTestCase {
|
||||||
Task task = mock(Task.class);
|
Task task = mock(Task.class);
|
||||||
User user = new User("username", "r1", "r2");
|
User user = new User("username", "r1", "r2");
|
||||||
Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null);
|
Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null);
|
||||||
doAnswer((i) -> {
|
doAnswer(i -> {
|
||||||
ActionListener callback =
|
final Object[] args = i.getArguments();
|
||||||
(ActionListener) i.getArguments()[3];
|
assertThat(args, arrayWithSize(4));
|
||||||
|
ActionListener callback = (ActionListener) args[args.length - 1];
|
||||||
callback.onResponse(authentication);
|
callback.onResponse(authentication);
|
||||||
return Void.TYPE;
|
return Void.TYPE;
|
||||||
}).when(authcService).authenticate(eq("_action"), eq(request), eq(SystemUser.INSTANCE), any(ActionListener.class));
|
}).when(authcService).authenticate(eq("_action"), eq(request), eq(SystemUser.INSTANCE), any(ActionListener.class));
|
||||||
|
|
|
@ -102,6 +102,7 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_PRIMARY_TERM;
|
import static org.elasticsearch.index.seqno.SequenceNumbers.UNASSIGNED_PRIMARY_TERM;
|
||||||
import static org.elasticsearch.test.SecurityTestsUtils.assertAuthenticationException;
|
import static org.elasticsearch.test.SecurityTestsUtils.assertAuthenticationException;
|
||||||
|
import static org.elasticsearch.test.TestMatchers.throwableWithMessage;
|
||||||
import static org.elasticsearch.xpack.core.security.support.Exceptions.authenticationError;
|
import static org.elasticsearch.xpack.core.security.support.Exceptions.authenticationError;
|
||||||
import static org.elasticsearch.xpack.security.authc.TokenServiceTests.mockGetTokenFromId;
|
import static org.elasticsearch.xpack.security.authc.TokenServiceTests.mockGetTokenFromId;
|
||||||
import static org.hamcrest.Matchers.arrayContaining;
|
import static org.hamcrest.Matchers.arrayContaining;
|
||||||
|
@ -252,7 +253,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
when(secondRealm.token(threadContext)).thenReturn(token);
|
when(secondRealm.token(threadContext)).thenReturn(token);
|
||||||
|
|
||||||
PlainActionFuture<Authentication> future = new PlainActionFuture<>();
|
PlainActionFuture<Authentication> future = new PlainActionFuture<>();
|
||||||
Authenticator authenticator = service.createAuthenticator("_action", message, null, future);
|
Authenticator authenticator = service.createAuthenticator("_action", message, true, future);
|
||||||
authenticator.extractToken((result) -> {
|
authenticator.extractToken((result) -> {
|
||||||
assertThat(result, notNullValue());
|
assertThat(result, notNullValue());
|
||||||
assertThat(result, is(token));
|
assertThat(result, is(token));
|
||||||
|
@ -263,7 +264,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
public void testTokenMissing() throws Exception {
|
public void testTokenMissing() throws Exception {
|
||||||
final String reqId = AuditUtil.getOrGenerateRequestId(threadContext);
|
final String reqId = AuditUtil.getOrGenerateRequestId(threadContext);
|
||||||
PlainActionFuture<Authentication> future = new PlainActionFuture<>();
|
PlainActionFuture<Authentication> future = new PlainActionFuture<>();
|
||||||
Authenticator authenticator = service.createAuthenticator("_action", message, null, future);
|
Authenticator authenticator = service.createAuthenticator("_action", message, true, future);
|
||||||
authenticator.extractToken((token) -> {
|
authenticator.extractToken((token) -> {
|
||||||
assertThat(token, nullValue());
|
assertThat(token, nullValue());
|
||||||
authenticator.handleNullToken();
|
authenticator.handleNullToken();
|
||||||
|
@ -289,7 +290,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
final String reqId = AuditUtil.getOrGenerateRequestId(threadContext);
|
final String reqId = AuditUtil.getOrGenerateRequestId(threadContext);
|
||||||
|
|
||||||
final AtomicBoolean completed = new AtomicBoolean(false);
|
final AtomicBoolean completed = new AtomicBoolean(false);
|
||||||
service.authenticate("_action", message, (User)null, ActionListener.wrap(result -> {
|
service.authenticate("_action", message, true, ActionListener.wrap(result -> {
|
||||||
assertThat(result, notNullValue());
|
assertThat(result, notNullValue());
|
||||||
assertThat(result.getUser(), is(user));
|
assertThat(result.getUser(), is(user));
|
||||||
assertThat(result.getLookedUpBy(), is(nullValue()));
|
assertThat(result.getLookedUpBy(), is(nullValue()));
|
||||||
|
@ -315,7 +316,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
|
|
||||||
// Authenticate against the normal chain. 1st Realm will be checked (and not pass) then 2nd realm will successfully authc
|
// Authenticate against the normal chain. 1st Realm will be checked (and not pass) then 2nd realm will successfully authc
|
||||||
final AtomicBoolean completed = new AtomicBoolean(false);
|
final AtomicBoolean completed = new AtomicBoolean(false);
|
||||||
service.authenticate("_action", message, (User)null, ActionListener.wrap(result -> {
|
service.authenticate("_action", message, true, ActionListener.wrap(result -> {
|
||||||
assertThat(result, notNullValue());
|
assertThat(result, notNullValue());
|
||||||
assertThat(result.getUser(), is(user));
|
assertThat(result.getUser(), is(user));
|
||||||
assertThat(result.getLookedUpBy(), is(nullValue()));
|
assertThat(result.getLookedUpBy(), is(nullValue()));
|
||||||
|
@ -331,7 +332,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
// Authenticate against the smart chain.
|
// Authenticate against the smart chain.
|
||||||
// "SecondRealm" will be at the top of the list and will successfully authc.
|
// "SecondRealm" will be at the top of the list and will successfully authc.
|
||||||
// "FirstRealm" will not be used
|
// "FirstRealm" will not be used
|
||||||
service.authenticate("_action", message, (User)null, ActionListener.wrap(result -> {
|
service.authenticate("_action", message, true, ActionListener.wrap(result -> {
|
||||||
assertThat(result, notNullValue());
|
assertThat(result, notNullValue());
|
||||||
assertThat(result.getUser(), is(user));
|
assertThat(result.getUser(), is(user));
|
||||||
assertThat(result.getLookedUpBy(), is(nullValue()));
|
assertThat(result.getLookedUpBy(), is(nullValue()));
|
||||||
|
@ -363,7 +364,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
// This will authenticate against the smart chain.
|
// This will authenticate against the smart chain.
|
||||||
// "SecondRealm" will be at the top of the list but will no longer authenticate the user.
|
// "SecondRealm" will be at the top of the list but will no longer authenticate the user.
|
||||||
// Then "FirstRealm" will be checked.
|
// Then "FirstRealm" will be checked.
|
||||||
service.authenticate("_action", message, (User)null, ActionListener.wrap(result -> {
|
service.authenticate("_action", message, true, ActionListener.wrap(result -> {
|
||||||
assertThat(result, notNullValue());
|
assertThat(result, notNullValue());
|
||||||
assertThat(result.getUser(), is(user));
|
assertThat(result.getUser(), is(user));
|
||||||
assertThat(result.getLookedUpBy(), is(nullValue()));
|
assertThat(result.getLookedUpBy(), is(nullValue()));
|
||||||
|
@ -432,7 +433,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
final String reqId = AuditUtil.getOrGenerateRequestId(threadContext);
|
final String reqId = AuditUtil.getOrGenerateRequestId(threadContext);
|
||||||
|
|
||||||
final AtomicBoolean completed = new AtomicBoolean(false);
|
final AtomicBoolean completed = new AtomicBoolean(false);
|
||||||
service.authenticate("_action", message, (User)null, ActionListener.wrap(result -> {
|
service.authenticate("_action", message, true, ActionListener.wrap(result -> {
|
||||||
assertThat(result, notNullValue());
|
assertThat(result, notNullValue());
|
||||||
assertThat(result.getUser(), is(user));
|
assertThat(result.getUser(), is(user));
|
||||||
assertThat(result.getLookedUpBy(), is(nullValue()));
|
assertThat(result.getLookedUpBy(), is(nullValue()));
|
||||||
|
@ -443,7 +444,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
assertTrue(completed.get());
|
assertTrue(completed.get());
|
||||||
|
|
||||||
completed.set(false);
|
completed.set(false);
|
||||||
service.authenticate("_action", message, (User)null, ActionListener.wrap(result -> {
|
service.authenticate("_action", message, true, ActionListener.wrap(result -> {
|
||||||
assertThat(result, notNullValue());
|
assertThat(result, notNullValue());
|
||||||
assertThat(result.getUser(), is(user));
|
assertThat(result.getUser(), is(user));
|
||||||
assertThat(result.getLookedUpBy(), is(nullValue()));
|
assertThat(result.getLookedUpBy(), is(nullValue()));
|
||||||
|
@ -474,7 +475,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
final String reqId = AuditUtil.getOrGenerateRequestId(threadContext);
|
final String reqId = AuditUtil.getOrGenerateRequestId(threadContext);
|
||||||
|
|
||||||
final AtomicBoolean completed = new AtomicBoolean(false);
|
final AtomicBoolean completed = new AtomicBoolean(false);
|
||||||
service.authenticate("_action", message, (User)null, ActionListener.wrap(result -> {
|
service.authenticate("_action", message, true, ActionListener.wrap(result -> {
|
||||||
assertThat(result, notNullValue());
|
assertThat(result, notNullValue());
|
||||||
assertThat(result.getUser(), is(user));
|
assertThat(result.getUser(), is(user));
|
||||||
assertThat(result.getAuthenticationType(), is(AuthenticationType.REALM));
|
assertThat(result.getAuthenticationType(), is(AuthenticationType.REALM));
|
||||||
|
@ -516,7 +517,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
when(firstRealm.token(threadContext)).thenReturn(null);
|
when(firstRealm.token(threadContext)).thenReturn(null);
|
||||||
when(secondRealm.token(threadContext)).thenReturn(null);
|
when(secondRealm.token(threadContext)).thenReturn(null);
|
||||||
|
|
||||||
Authenticator authenticator = service.createAuthenticator(restRequest, mock(ActionListener.class));
|
Authenticator authenticator = service.createAuthenticator(restRequest, true, mock(ActionListener.class));
|
||||||
authenticator.extractToken((token) -> {
|
authenticator.extractToken((token) -> {
|
||||||
assertThat(token, nullValue());
|
assertThat(token, nullValue());
|
||||||
});
|
});
|
||||||
|
@ -613,14 +614,19 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
|
|
||||||
public void testAuthenticateTransportSuccess() throws Exception {
|
public void testAuthenticateTransportSuccess() throws Exception {
|
||||||
final String reqId = AuditUtil.getOrGenerateRequestId(threadContext);
|
final String reqId = AuditUtil.getOrGenerateRequestId(threadContext);
|
||||||
User user = new User("username", "r1", "r2");
|
final User user = new User("username", "r1", "r2");
|
||||||
User fallback = randomBoolean() ? SystemUser.INSTANCE : null;
|
final Consumer<ActionListener<Authentication>> authenticate;
|
||||||
|
if (randomBoolean()) {
|
||||||
|
authenticate = listener -> service.authenticate("_action", message, SystemUser.INSTANCE, listener);
|
||||||
|
} else {
|
||||||
|
authenticate = listener -> service.authenticate("_action", message, true, listener);
|
||||||
|
}
|
||||||
when(firstRealm.token(threadContext)).thenReturn(token);
|
when(firstRealm.token(threadContext)).thenReturn(token);
|
||||||
when(firstRealm.supports(token)).thenReturn(true);
|
when(firstRealm.supports(token)).thenReturn(true);
|
||||||
mockAuthenticate(firstRealm, token, user);
|
mockAuthenticate(firstRealm, token, user);
|
||||||
|
|
||||||
final AtomicBoolean completed = new AtomicBoolean(false);
|
final AtomicBoolean completed = new AtomicBoolean(false);
|
||||||
service.authenticate("_action", message, fallback, ActionListener.wrap(result -> {
|
authenticate.accept(ActionListener.wrap(result -> {
|
||||||
assertThat(result, notNullValue());
|
assertThat(result, notNullValue());
|
||||||
assertThat(result.getUser(), sameInstance(user));
|
assertThat(result.getUser(), sameInstance(user));
|
||||||
assertThreadContextContainsAuthentication(result);
|
assertThreadContextContainsAuthentication(result);
|
||||||
|
@ -663,14 +669,14 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
final SetOnce<String> authHeaderRef = new SetOnce<>();
|
final SetOnce<String> authHeaderRef = new SetOnce<>();
|
||||||
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
|
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
|
||||||
service.authenticate("_action", message, SystemUser.INSTANCE, ActionListener.wrap(authentication -> {
|
service.authenticate("_action", message, SystemUser.INSTANCE, ActionListener.wrap(authentication -> {
|
||||||
assertThat(authentication, notNullValue());
|
assertThat(authentication, notNullValue());
|
||||||
assertThat(authentication.getUser(), sameInstance(user1));
|
assertThat(authentication.getUser(), sameInstance(user1));
|
||||||
assertThat(authentication.getAuthenticationType(), is(AuthenticationType.REALM));
|
assertThat(authentication.getAuthenticationType(), is(AuthenticationType.REALM));
|
||||||
assertThreadContextContainsAuthentication(authentication);
|
assertThreadContextContainsAuthentication(authentication);
|
||||||
authRef.set(authentication);
|
authRef.set(authentication);
|
||||||
authHeaderRef.set(threadContext.getHeader(AuthenticationField.AUTHENTICATION_KEY));
|
authHeaderRef.set(threadContext.getHeader(AuthenticationField.AUTHENTICATION_KEY));
|
||||||
setCompletedToTrue(completed);
|
setCompletedToTrue(completed);
|
||||||
}, this::logAndFail));
|
}, this::logAndFail));
|
||||||
}
|
}
|
||||||
assertTrue(completed.compareAndSet(true, false));
|
assertTrue(completed.compareAndSet(true, false));
|
||||||
reset(firstRealm);
|
reset(firstRealm);
|
||||||
|
@ -687,10 +693,10 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
threadContext1.putTransient(AuthenticationField.AUTHENTICATION_KEY, authRef.get());
|
threadContext1.putTransient(AuthenticationField.AUTHENTICATION_KEY, authRef.get());
|
||||||
threadContext1.putHeader(AuthenticationField.AUTHENTICATION_KEY, authHeaderRef.get());
|
threadContext1.putHeader(AuthenticationField.AUTHENTICATION_KEY, authHeaderRef.get());
|
||||||
service.authenticate("_action", message1, SystemUser.INSTANCE, ActionListener.wrap(ctxAuth -> {
|
service.authenticate("_action", message1, SystemUser.INSTANCE, ActionListener.wrap(ctxAuth -> {
|
||||||
assertThat(ctxAuth, sameInstance(authRef.get()));
|
assertThat(ctxAuth, sameInstance(authRef.get()));
|
||||||
assertThat(threadContext1.getHeader(AuthenticationField.AUTHENTICATION_KEY), sameInstance(authHeaderRef.get()));
|
assertThat(threadContext1.getHeader(AuthenticationField.AUTHENTICATION_KEY), sameInstance(authHeaderRef.get()));
|
||||||
setCompletedToTrue(completed);
|
setCompletedToTrue(completed);
|
||||||
}, this::logAndFail));
|
}, this::logAndFail));
|
||||||
assertTrue(completed.compareAndSet(true, false));
|
assertTrue(completed.compareAndSet(true, false));
|
||||||
verifyZeroInteractions(firstRealm);
|
verifyZeroInteractions(firstRealm);
|
||||||
reset(firstRealm);
|
reset(firstRealm);
|
||||||
|
@ -722,11 +728,11 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
new DefaultAuthenticationFailureHandler(Collections.emptyMap()), threadPool2, new AnonymousUser(Settings.EMPTY),
|
new DefaultAuthenticationFailureHandler(Collections.emptyMap()), threadPool2, new AnonymousUser(Settings.EMPTY),
|
||||||
tokenService, apiKeyService);
|
tokenService, apiKeyService);
|
||||||
service.authenticate("_action", new InternalMessage(), SystemUser.INSTANCE, ActionListener.wrap(result -> {
|
service.authenticate("_action", new InternalMessage(), SystemUser.INSTANCE, ActionListener.wrap(result -> {
|
||||||
assertThat(result, notNullValue());
|
assertThat(result, notNullValue());
|
||||||
assertThat(result.getUser(), equalTo(user1));
|
assertThat(result.getUser(), equalTo(user1));
|
||||||
assertThat(result.getAuthenticationType(), is(AuthenticationType.REALM));
|
assertThat(result.getAuthenticationType(), is(AuthenticationType.REALM));
|
||||||
setCompletedToTrue(completed);
|
setCompletedToTrue(completed);
|
||||||
}, this::logAndFail));
|
}, this::logAndFail));
|
||||||
assertTrue(completed.get());
|
assertTrue(completed.get());
|
||||||
verifyZeroInteractions(firstRealm);
|
verifyZeroInteractions(firstRealm);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -825,6 +831,33 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
verifyNoMoreInteractions(auditTrail);
|
verifyNoMoreInteractions(auditTrail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testAuthenticateRestRequestDisallowAnonymous() throws Exception {
|
||||||
|
final String username = randomBoolean() ? AnonymousUser.DEFAULT_ANONYMOUS_USERNAME : "_anon_" + randomAlphaOfLengthBetween(2, 6);
|
||||||
|
final Settings.Builder builder = Settings.builder()
|
||||||
|
.putList(AnonymousUser.ROLES_SETTING.getKey(), "r1", "r2", "r3");
|
||||||
|
if (username.equals(AnonymousUser.DEFAULT_ANONYMOUS_USERNAME) == false) {
|
||||||
|
builder.put(AnonymousUser.USERNAME_SETTING.getKey(), username);
|
||||||
|
}
|
||||||
|
Settings settings = builder.build();
|
||||||
|
|
||||||
|
final AnonymousUser anonymousUser = new AnonymousUser(settings);
|
||||||
|
service = new AuthenticationService(settings, realms, auditTrail, new DefaultAuthenticationFailureHandler(Collections.emptyMap()),
|
||||||
|
threadPool, anonymousUser, tokenService, apiKeyService);
|
||||||
|
RestRequest request = new FakeRestRequest();
|
||||||
|
|
||||||
|
PlainActionFuture<Authentication> future = new PlainActionFuture<>();
|
||||||
|
service.authenticate(request, false, future);
|
||||||
|
final ElasticsearchSecurityException ex = expectThrows(ElasticsearchSecurityException.class, future::actionGet);
|
||||||
|
|
||||||
|
assertThat(ex, notNullValue());
|
||||||
|
assertThat(ex, throwableWithMessage(containsString("missing authentication credentials for REST request")));
|
||||||
|
assertThat(threadContext.getTransient(AuthenticationField.AUTHENTICATION_KEY), nullValue());
|
||||||
|
assertThat(threadContext.getHeader(AuthenticationField.AUTHENTICATION_KEY), nullValue());
|
||||||
|
String reqId = expectAuditRequestId();
|
||||||
|
verify(auditTrail).anonymousAccessDenied(reqId, request);
|
||||||
|
verifyNoMoreInteractions(auditTrail);
|
||||||
|
}
|
||||||
|
|
||||||
public void testAnonymousUserTransportNoDefaultUser() throws Exception {
|
public void testAnonymousUserTransportNoDefaultUser() throws Exception {
|
||||||
Settings settings = Settings.builder()
|
Settings settings = Settings.builder()
|
||||||
.putList(AnonymousUser.ROLES_SETTING.getKey(), "r1", "r2", "r3")
|
.putList(AnonymousUser.ROLES_SETTING.getKey(), "r1", "r2", "r3")
|
||||||
|
@ -1072,7 +1105,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
|
|
||||||
// we do not actually go async
|
// we do not actually go async
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
service.authenticate("_action", message, (User)null, listener);
|
service.authenticate("_action", message, true, listener);
|
||||||
} else {
|
} else {
|
||||||
service.authenticate(restRequest, listener);
|
service.authenticate(restRequest, listener);
|
||||||
}
|
}
|
||||||
|
@ -1111,7 +1144,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
|
|
||||||
// call service asynchronously but it doesn't actually go async
|
// call service asynchronously but it doesn't actually go async
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
service.authenticate("_action", message, (User)null, listener);
|
service.authenticate("_action", message, true, listener);
|
||||||
} else {
|
} else {
|
||||||
service.authenticate(restRequest, listener);
|
service.authenticate(restRequest, listener);
|
||||||
}
|
}
|
||||||
|
@ -1221,14 +1254,14 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
when(securityIndex.indexExists()).thenReturn(true);
|
when(securityIndex.indexExists()).thenReturn(true);
|
||||||
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
|
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
|
||||||
threadContext.putHeader("Authorization", "Bearer " + token);
|
threadContext.putHeader("Authorization", "Bearer " + token);
|
||||||
service.authenticate("_action", message, (User)null, ActionListener.wrap(result -> {
|
service.authenticate("_action", message, true, ActionListener.wrap(result -> {
|
||||||
assertThat(result, notNullValue());
|
assertThat(result, notNullValue());
|
||||||
assertThat(result.getUser(), is(user));
|
assertThat(result.getUser(), is(user));
|
||||||
assertThat(result.getLookedUpBy(), is(nullValue()));
|
assertThat(result.getLookedUpBy(), is(nullValue()));
|
||||||
assertThat(result.getAuthenticatedBy(), is(notNullValue()));
|
assertThat(result.getAuthenticatedBy(), is(notNullValue()));
|
||||||
assertThat(result.getAuthenticationType(), is(AuthenticationType.TOKEN));
|
assertThat(result.getAuthenticationType(), is(AuthenticationType.TOKEN));
|
||||||
setCompletedToTrue(completed);
|
setCompletedToTrue(completed);
|
||||||
}, this::logAndFail));
|
}, this::logAndFail));
|
||||||
}
|
}
|
||||||
assertTrue(completed.get());
|
assertTrue(completed.get());
|
||||||
verify(auditTrail).authenticationSuccess(anyString(), eq("realm"), eq(user), eq("_action"), same(message));
|
verify(auditTrail).authenticationSuccess(anyString(), eq("realm"), eq(user), eq("_action"), same(message));
|
||||||
|
@ -1248,7 +1281,7 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
AtomicBoolean success = new AtomicBoolean(false);
|
AtomicBoolean success = new AtomicBoolean(false);
|
||||||
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
|
try (ThreadContext.StoredContext ignore = threadContext.stashContext()) {
|
||||||
threadContext.putHeader("Authorization", "Bearer " + Base64.getEncoder().encodeToString(randomBytes));
|
threadContext.putHeader("Authorization", "Bearer " + Base64.getEncoder().encodeToString(randomBytes));
|
||||||
service.authenticate("_action", message, (User)null, ActionListener.wrap(result -> {
|
service.authenticate("_action", message, true, ActionListener.wrap(result -> {
|
||||||
assertThat(result, notNullValue());
|
assertThat(result, notNullValue());
|
||||||
assertThat(result.getUser(), is(user));
|
assertThat(result.getUser(), is(user));
|
||||||
assertThat(result.getLookedUpBy(), is(nullValue()));
|
assertThat(result.getLookedUpBy(), is(nullValue()));
|
||||||
|
@ -1462,7 +1495,11 @@ public class AuthenticationServiceTests extends ESTestCase {
|
||||||
|
|
||||||
private Authentication authenticateBlocking(String action, TransportMessage message, User fallbackUser) {
|
private Authentication authenticateBlocking(String action, TransportMessage message, User fallbackUser) {
|
||||||
PlainActionFuture<Authentication> future = new PlainActionFuture<>();
|
PlainActionFuture<Authentication> future = new PlainActionFuture<>();
|
||||||
service.authenticate(action, message, fallbackUser, future);
|
if (fallbackUser == null) {
|
||||||
|
service.authenticate(action, message, true, future);
|
||||||
|
} else {
|
||||||
|
service.authenticate(action, message, fallbackUser, future);
|
||||||
|
}
|
||||||
return future.actionGet();
|
return future.actionGet();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ import java.util.Collections;
|
||||||
|
|
||||||
import static org.elasticsearch.xpack.core.security.support.Exceptions.authenticationError;
|
import static org.elasticsearch.xpack.core.security.support.Exceptions.authenticationError;
|
||||||
import static org.elasticsearch.xpack.core.security.support.Exceptions.authorizationError;
|
import static org.elasticsearch.xpack.core.security.support.Exceptions.authorizationError;
|
||||||
|
import static org.hamcrest.Matchers.arrayWithSize;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.mockito.Matchers.any;
|
import static org.mockito.Matchers.any;
|
||||||
import static org.mockito.Matchers.eq;
|
import static org.mockito.Matchers.eq;
|
||||||
|
@ -77,12 +78,13 @@ public class ServerTransportFilterTests extends ESTestCase {
|
||||||
Authentication authentication = mock(Authentication.class);
|
Authentication authentication = mock(Authentication.class);
|
||||||
when(authentication.getVersion()).thenReturn(Version.CURRENT);
|
when(authentication.getVersion()).thenReturn(Version.CURRENT);
|
||||||
when(authentication.getUser()).thenReturn(SystemUser.INSTANCE);
|
when(authentication.getUser()).thenReturn(SystemUser.INSTANCE);
|
||||||
doAnswer((i) -> {
|
doAnswer(i -> {
|
||||||
ActionListener callback =
|
final Object[] args = i.getArguments();
|
||||||
(ActionListener) i.getArguments()[3];
|
assertThat(args, arrayWithSize(4));
|
||||||
|
ActionListener callback = (ActionListener) args[args.length - 1];
|
||||||
callback.onResponse(authentication);
|
callback.onResponse(authentication);
|
||||||
return Void.TYPE;
|
return Void.TYPE;
|
||||||
}).when(authcService).authenticate(eq("_action"), eq(request), eq((User)null), any(ActionListener.class));
|
}).when(authcService).authenticate(eq("_action"), eq(request), eq(true), any(ActionListener.class));
|
||||||
ServerTransportFilter filter = getClientOrNodeFilter();
|
ServerTransportFilter filter = getClientOrNodeFilter();
|
||||||
PlainActionFuture<Void> future = new PlainActionFuture<>();
|
PlainActionFuture<Void> future = new PlainActionFuture<>();
|
||||||
filter.inbound("_action", request, channel, future);
|
filter.inbound("_action", request, channel, future);
|
||||||
|
@ -98,12 +100,13 @@ public class ServerTransportFilterTests extends ESTestCase {
|
||||||
Authentication authentication = mock(Authentication.class);
|
Authentication authentication = mock(Authentication.class);
|
||||||
when(authentication.getVersion()).thenReturn(Version.CURRENT);
|
when(authentication.getVersion()).thenReturn(Version.CURRENT);
|
||||||
when(authentication.getUser()).thenReturn(SystemUser.INSTANCE);
|
when(authentication.getUser()).thenReturn(SystemUser.INSTANCE);
|
||||||
doAnswer((i) -> {
|
doAnswer(i -> {
|
||||||
ActionListener callback =
|
final Object[] args = i.getArguments();
|
||||||
(ActionListener) i.getArguments()[3];
|
assertThat(args, arrayWithSize(4));
|
||||||
|
ActionListener callback = (ActionListener) args[args.length - 1];
|
||||||
callback.onResponse(authentication);
|
callback.onResponse(authentication);
|
||||||
return Void.TYPE;
|
return Void.TYPE;
|
||||||
}).when(authcService).authenticate(eq(action), eq(request), eq((User)null), any(ActionListener.class));
|
}).when(authcService).authenticate(eq(action), eq(request), eq(true), any(ActionListener.class));
|
||||||
ServerTransportFilter filter = getClientOrNodeFilter();
|
ServerTransportFilter filter = getClientOrNodeFilter();
|
||||||
PlainActionFuture listener = mock(PlainActionFuture.class);
|
PlainActionFuture listener = mock(PlainActionFuture.class);
|
||||||
filter.inbound(action, request, channel, listener);
|
filter.inbound(action, request, channel, listener);
|
||||||
|
@ -118,12 +121,13 @@ public class ServerTransportFilterTests extends ESTestCase {
|
||||||
public void testInboundAuthenticationException() throws Exception {
|
public void testInboundAuthenticationException() throws Exception {
|
||||||
TransportRequest request = mock(TransportRequest.class);
|
TransportRequest request = mock(TransportRequest.class);
|
||||||
Exception authE = authenticationError("authc failed");
|
Exception authE = authenticationError("authc failed");
|
||||||
doAnswer((i) -> {
|
doAnswer(i -> {
|
||||||
ActionListener callback =
|
final Object[] args = i.getArguments();
|
||||||
(ActionListener) i.getArguments()[3];
|
assertThat(args, arrayWithSize(4));
|
||||||
|
ActionListener callback = (ActionListener) args[args.length - 1];
|
||||||
callback.onFailure(authE);
|
callback.onFailure(authE);
|
||||||
return Void.TYPE;
|
return Void.TYPE;
|
||||||
}).when(authcService).authenticate(eq("_action"), eq(request), eq((User)null), any(ActionListener.class));
|
}).when(authcService).authenticate(eq("_action"), eq(request), eq(true), any(ActionListener.class));
|
||||||
ServerTransportFilter filter = getClientOrNodeFilter();
|
ServerTransportFilter filter = getClientOrNodeFilter();
|
||||||
try {
|
try {
|
||||||
PlainActionFuture<Void> future = new PlainActionFuture<>();
|
PlainActionFuture<Void> future = new PlainActionFuture<>();
|
||||||
|
@ -140,12 +144,13 @@ public class ServerTransportFilterTests extends ESTestCase {
|
||||||
ServerTransportFilter filter = getClientOrNodeFilter();
|
ServerTransportFilter filter = getClientOrNodeFilter();
|
||||||
TransportRequest request = mock(TransportRequest.class);
|
TransportRequest request = mock(TransportRequest.class);
|
||||||
Authentication authentication = mock(Authentication.class);
|
Authentication authentication = mock(Authentication.class);
|
||||||
doAnswer((i) -> {
|
doAnswer(i -> {
|
||||||
ActionListener callback =
|
final Object[] args = i.getArguments();
|
||||||
(ActionListener) i.getArguments()[3];
|
assertThat(args, arrayWithSize(4));
|
||||||
|
ActionListener callback = (ActionListener) args[args.length - 1];
|
||||||
callback.onResponse(authentication);
|
callback.onResponse(authentication);
|
||||||
return Void.TYPE;
|
return Void.TYPE;
|
||||||
}).when(authcService).authenticate(eq("_action"), eq(request), eq((User)null), any(ActionListener.class));
|
}).when(authcService).authenticate(eq("_action"), eq(request), eq(true), any(ActionListener.class));
|
||||||
when(authentication.getVersion()).thenReturn(Version.CURRENT);
|
when(authentication.getVersion()).thenReturn(Version.CURRENT);
|
||||||
when(authentication.getUser()).thenReturn(XPackUser.INSTANCE);
|
when(authentication.getUser()).thenReturn(XPackUser.INSTANCE);
|
||||||
PlainActionFuture<Void> future = new PlainActionFuture<>();
|
PlainActionFuture<Void> future = new PlainActionFuture<>();
|
||||||
|
@ -177,25 +182,27 @@ public class ServerTransportFilterTests extends ESTestCase {
|
||||||
ServerTransportFilter filter = getNodeFilter(true);
|
ServerTransportFilter filter = getNodeFilter(true);
|
||||||
TransportRequest request = mock(TransportRequest.class);
|
TransportRequest request = mock(TransportRequest.class);
|
||||||
Authentication authentication = new Authentication(new User("test", "superuser"), new RealmRef("test", "test", "node1"), null);
|
Authentication authentication = new Authentication(new User("test", "superuser"), new RealmRef("test", "test", "node1"), null);
|
||||||
doAnswer((i) -> {
|
doAnswer(i -> {
|
||||||
ActionListener callback =
|
final Object[] args = i.getArguments();
|
||||||
(ActionListener) i.getArguments()[3];
|
assertThat(args, arrayWithSize(4));
|
||||||
|
ActionListener callback = (ActionListener) args[args.length - 1];
|
||||||
callback.onResponse(authentication);
|
callback.onResponse(authentication);
|
||||||
return Void.TYPE;
|
return Void.TYPE;
|
||||||
}).when(authcService).authenticate(eq(internalAction), eq(request), eq((User)null), any(ActionListener.class));
|
}).when(authcService).authenticate(eq(internalAction), eq(request), eq(true), any(ActionListener.class));
|
||||||
doAnswer((i) -> {
|
doAnswer((i) -> {
|
||||||
ActionListener callback =
|
final Object[] args = i.getArguments();
|
||||||
(ActionListener) i.getArguments()[3];
|
assertThat(args, arrayWithSize(4));
|
||||||
|
ActionListener callback = (ActionListener) args[args.length - 1];
|
||||||
callback.onResponse(authentication);
|
callback.onResponse(authentication);
|
||||||
return Void.TYPE;
|
return Void.TYPE;
|
||||||
}).when(authcService).authenticate(eq(nodeOrShardAction), eq(request), eq((User)null), any(ActionListener.class));
|
}).when(authcService).authenticate(eq(nodeOrShardAction), eq(request), eq(true), any(ActionListener.class));
|
||||||
|
|
||||||
filter.inbound(internalAction, request, channel, new PlainActionFuture<>());
|
filter.inbound(internalAction, request, channel, new PlainActionFuture<>());
|
||||||
verify(authcService).authenticate(eq(internalAction), eq(request), eq((User)null), any(ActionListener.class));
|
verify(authcService).authenticate(eq(internalAction), eq(request), eq(true), any(ActionListener.class));
|
||||||
verify(authzService).authorize(eq(authentication), eq(internalAction), eq(request), any(ActionListener.class));
|
verify(authzService).authorize(eq(authentication), eq(internalAction), eq(request), any(ActionListener.class));
|
||||||
|
|
||||||
filter.inbound(nodeOrShardAction, request, channel, new PlainActionFuture<>());
|
filter.inbound(nodeOrShardAction, request, channel, new PlainActionFuture<>());
|
||||||
verify(authcService).authenticate(eq(nodeOrShardAction), eq(request), eq((User)null), any(ActionListener.class));
|
verify(authcService).authenticate(eq(nodeOrShardAction), eq(request), eq(true), any(ActionListener.class));
|
||||||
verify(authzService).authorize(eq(authentication), eq(nodeOrShardAction), eq(request), any(ActionListener.class));
|
verify(authzService).authorize(eq(authentication), eq(nodeOrShardAction), eq(request), any(ActionListener.class));
|
||||||
verifyNoMoreInteractions(authcService, authzService);
|
verifyNoMoreInteractions(authcService, authzService);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue