diff --git a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/shield/authz/AuthorizationUtilsTests.java b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/shield/authz/AuthorizationUtilsTests.java index a27290ca161..cdff7aa1e87 100644 --- a/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/shield/authz/AuthorizationUtilsTests.java +++ b/elasticsearch/x-pack/license-plugin/src/test/java/org/elasticsearch/shield/authz/AuthorizationUtilsTests.java @@ -7,9 +7,10 @@ package org.elasticsearch.shield.authz; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.shield.authc.Authentication; +import org.elasticsearch.shield.authc.Authentication.RealmRef; import org.elasticsearch.shield.user.SystemUser; import org.elasticsearch.shield.user.User; -import org.elasticsearch.shield.authc.InternalAuthenticationService; import org.elasticsearch.test.ESTestCase; import org.junit.Before; @@ -33,21 +34,24 @@ public class AuthorizationUtilsTests extends ESTestCase { public void testSystemUserSwitchWithNullorSystemUser() { if (randomBoolean()) { - threadContext.putTransient(InternalAuthenticationService.USER_KEY, SystemUser.INSTANCE); + threadContext.putTransient(Authentication.AUTHENTICATION_KEY, + new Authentication(SystemUser.INSTANCE, new RealmRef("test", "test", "foo"), null)); } assertThat(AuthorizationUtils.shouldReplaceUserWithSystem(threadContext, "internal:something"), is(true)); } public void testSystemUserSwitchWithNonSystemUser() { User user = new User(randomAsciiOfLength(6), new String[] {}); - threadContext.putTransient(InternalAuthenticationService.USER_KEY, user); + Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null); + threadContext.putTransient(Authentication.AUTHENTICATION_KEY, authentication); threadContext.putTransient(InternalAuthorizationService.ORIGINATING_ACTION_KEY, randomFrom("indices:foo", "cluster:bar")); assertThat(AuthorizationUtils.shouldReplaceUserWithSystem(threadContext, "internal:something"), is(true)); } public void testSystemUserSwitchWithNonSystemUserAndInternalAction() { User user = new User(randomAsciiOfLength(6), new String[] {}); - threadContext.putTransient(InternalAuthenticationService.USER_KEY, user); + Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null); + threadContext.putTransient(Authentication.AUTHENTICATION_KEY, authentication); threadContext.putTransient(InternalAuthorizationService.ORIGINATING_ACTION_KEY, randomFrom("internal:foo/bar")); assertThat(AuthorizationUtils.shouldReplaceUserWithSystem(threadContext, "internal:something"), is(false)); } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/InternalClient.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/InternalClient.java index f939ceb022f..f5f889737ea 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/InternalClient.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/InternalClient.java @@ -62,7 +62,7 @@ public abstract class InternalClient extends FilterClient { try (ThreadContext.StoredContext ctx = threadPool().getThreadContext().stashContext()) { try { - authcService.attachUserHeaderIfMissing(XPackUser.INSTANCE); + authcService.attachUserIfMissing(XPackUser.INSTANCE); } catch (IOException ioe) { throw new ElasticsearchException("failed to attach internal user to request", ioe); } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/SecurityContext.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/SecurityContext.java index e065e0ca983..4a5e82ead7c 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/SecurityContext.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/SecurityContext.java @@ -8,6 +8,7 @@ package org.elasticsearch.shield; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.shield.authc.Authentication; import org.elasticsearch.shield.authc.AuthenticationService; import org.elasticsearch.shield.user.User; import org.elasticsearch.threadpool.ThreadPool; @@ -26,6 +27,12 @@ public interface SecurityContext { User getUser(); + Authentication getAuthentication(); + + default boolean hasAuthentication() { + return getAuthentication() != null; + } + class Insecure implements SecurityContext { public static final Insecure INSTANCE = new Insecure(); @@ -51,6 +58,11 @@ public interface SecurityContext { public User getUser() { return null; } + + @Override + public Authentication getAuthentication() { + return null; + } } class Secure implements SecurityContext { @@ -82,14 +94,20 @@ public interface SecurityContext { @Override public User getUser() { - return authcService.getCurrentUser(); + Authentication authentication = authcService.getCurrentAuthentication(); + return authentication == null ? null : authentication.getUser(); + } + + @Override + public Authentication getAuthentication() { + return authcService.getCurrentAuthentication(); } private void setUser(User user) { try { - authcService.attachUserHeaderIfMissing(user); - } catch (IOException e) { - throw new ElasticsearchException("failed to attach watcher user to request", e); + authcService.attachUserIfMissing(user); + } catch (IOException | IllegalArgumentException e) { + throw new ElasticsearchException("failed to attach user to request", e); } } } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/filter/ShieldActionFilter.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/filter/ShieldActionFilter.java index 81466d9583f..4ff41b15cd0 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/filter/ShieldActionFilter.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/filter/ShieldActionFilter.java @@ -20,13 +20,14 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.license.plugin.core.LicenseUtils; import org.elasticsearch.shield.Security; +import org.elasticsearch.shield.SecurityContext; import org.elasticsearch.shield.action.ShieldActionMapper; +import org.elasticsearch.shield.authc.Authentication; import org.elasticsearch.shield.user.SystemUser; import org.elasticsearch.shield.user.User; import org.elasticsearch.shield.action.interceptor.RequestInterceptor; import org.elasticsearch.shield.audit.AuditTrail; import org.elasticsearch.shield.authc.AuthenticationService; -import org.elasticsearch.shield.authc.InternalAuthenticationService; import org.elasticsearch.shield.authz.AuthorizationService; import org.elasticsearch.shield.authz.AuthorizationUtils; import org.elasticsearch.shield.authz.privilege.HealthAndStatsPrivilege; @@ -58,11 +59,13 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte private final Set requestInterceptors; private final SecurityLicenseState licenseState; private final ThreadContext threadContext; + private final SecurityContext securityContext; @Inject public ShieldActionFilter(Settings settings, AuthenticationService authcService, AuthorizationService authzService, CryptoService cryptoService, AuditTrail auditTrail, SecurityLicenseState licenseState, - ShieldActionMapper actionMapper, Set requestInterceptors, ThreadPool threadPool) { + ShieldActionMapper actionMapper, Set requestInterceptors, ThreadPool threadPool, + SecurityContext securityContext) { super(settings); this.authcService = authcService; this.authzService = authzService; @@ -72,6 +75,7 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte this.licenseState = licenseState; this.requestInterceptors = requestInterceptors; this.threadContext = threadPool.getThreadContext(); + this.securityContext = securityContext; } @Override @@ -91,8 +95,7 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte // only restore the context if it is not empty. This is needed because sometimes a response is sent to the user // and then a cleanup action is executed (like for search without a scroll) final ThreadContext.StoredContext original = threadContext.newStoredContext(); - final boolean restoreOriginalContext = threadContext.getHeader(InternalAuthenticationService.USER_KEY) != null || - threadContext.getTransient(InternalAuthenticationService.USER_KEY) != null; + final boolean restoreOriginalContext = securityContext.hasAuthentication(); try { if (licenseState.authenticationAndAuthorizationEnabled()) { if (AuthorizationUtils.shouldReplaceUserWithSystem(threadContext, action)) { @@ -133,9 +136,11 @@ public class ShieldActionFilter extends AbstractComponent implements ActionFilte the {@link Rest} filter and the {@link ServerTransport} filter respectively), it's safe to assume a system user here if a request is not associated with any other user. */ - String shieldAction = actionMapper.action(action, request); - User user = authcService.authenticate(shieldAction, request, SystemUser.INSTANCE); - authzService.authorize(user, shieldAction, request); + final String shieldAction = actionMapper.action(action, request); + Authentication authentication = authcService.authenticate(shieldAction, request, SystemUser.INSTANCE); + assert authentication != null; + authzService.authorize(authentication, shieldAction, request); + final User user = authentication.getUser(); request = unsign(user, shieldAction, request); /* diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/AuditTrail.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/AuditTrail.java index a6efbc13cea..196570f4fe6 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/AuditTrail.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/AuditTrail.java @@ -94,6 +94,10 @@ public interface AuditTrail { @Override public void runAsDenied(User user, String action, TransportMessage message) { } + + @Override + public void runAsDenied(User user, RestRequest request) { + } }; String name(); @@ -131,4 +135,6 @@ public interface AuditTrail { void runAsGranted(User user, String action, TransportMessage message); void runAsDenied(User user, String action, TransportMessage message); + + void runAsDenied(User user, RestRequest request); } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/AuditTrailService.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/AuditTrailService.java index c806f511fec..d717811c1fd 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/AuditTrailService.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/AuditTrailService.java @@ -188,4 +188,13 @@ public class AuditTrailService extends AbstractComponent implements AuditTrail { } } } + + @Override + public void runAsDenied(User user, RestRequest request) { + if (securityLicenseState.auditingEnabled()) { + for (AuditTrail auditTrail : auditTrails) { + auditTrail.runAsDenied(user, request); + } + } + } } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/index/IndexAuditTrail.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/index/IndexAuditTrail.java index da8163139b1..e7b70374089 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/index/IndexAuditTrail.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/index/IndexAuditTrail.java @@ -512,7 +512,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl public void runAsGranted(User user, String action, TransportMessage message) { if (events.contains(RUN_AS_GRANTED)) { try { - enqueue(message("run_as_granted", action, user, null, message), "access_granted"); + enqueue(message("run_as_granted", action, user, null, message), "run_as_granted"); } catch (Exception e) { logger.warn("failed to index audit event: [run_as_granted]", e); } @@ -523,7 +523,18 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl public void runAsDenied(User user, String action, TransportMessage message) { if (events.contains(RUN_AS_DENIED)) { try { - enqueue(message("run_as_denied", action, user, null, message), "access_granted"); + enqueue(message("run_as_denied", action, user, null, message), "run_as_denied"); + } catch (Exception e) { + logger.warn("failed to index audit event: [run_as_denied]", e); + } + } + } + + @Override + public void runAsDenied(User user, RestRequest request) { + if (events.contains(RUN_AS_DENIED)) { + try { + enqueue(message("run_as_denied", user, request), "run_as_denied"); } catch (Exception e) { logger.warn("failed to index audit event: [run_as_denied]", e); } @@ -620,6 +631,26 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl return msg.end(); } + private Message message(String type, User user, RestRequest request) throws Exception { + + Message msg = new Message().start(); + common("rest", type, msg.builder); + + msg.builder.field(Field.PRINCIPAL, user.principal()); + msg.builder.field(Field.REQUEST_BODY, restRequestContent(request)); + msg.builder.field(Field.ORIGIN_TYPE, "rest"); + SocketAddress address = request.getRemoteAddress(); + if (address instanceof InetSocketAddress) { + msg.builder.field(Field.ORIGIN_ADDRESS, NetworkAddress.format(((InetSocketAddress) request.getRemoteAddress()) + .getAddress())); + } else { + msg.builder.field(Field.ORIGIN_ADDRESS, address); + } + msg.builder.field(Field.URI, request.uri()); + + return msg.end(); + } + private Message message(String layer, String type, InetAddress originAddress, String profile, ShieldIpFilterRule rule) throws IOException { diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/logfile/LoggingAuditTrail.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/logfile/LoggingAuditTrail.java index 4f4f6942883..5df0f9f2d5e 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/logfile/LoggingAuditTrail.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/logfile/LoggingAuditTrail.java @@ -384,6 +384,17 @@ public class LoggingAuditTrail extends AbstractLifecycleComponent - * The order by which the realms are checked is defined in {@link Realms}. - * - * @param request the request to authenticate - * @param fallbackUser The user to assume if there is not other user attached to the message - * @return The authenticated user - * @throws ElasticsearchSecurityException If none of the configured realms successfully authenticated the - * request - */ - User authenticateWithRealms(AuditableRequest request, User fallbackUser) throws ElasticsearchSecurityException { - AuthenticationToken token; - try { - token = token(request); - } catch (Exception e) { - if (logger.isDebugEnabled()) { - logger.debug("failed to extract token from request: [{}]", e, request); - } else { - logger.warn("failed to extract token from request: [{}]", request, e.getMessage()); - } - throw request.exceptionProcessingRequest(e); + Authenticator createAuthenticator(RestRequest request) { + return new Authenticator(request); + } + + Authenticator createAuthenticator(String action, TransportMessage message, User fallbackUser) { + return new Authenticator(action, message, fallbackUser); + } + + class Authenticator { + + private final AuditableRequest request; + private final User fallbackUser; + + private RealmRef authenticatedBy = null; + private RealmRef lookedupBy = null; + + Authenticator(RestRequest request) { + this.request = new Rest(request); + this.fallbackUser = null; } - if (token == null) { - if (fallbackUser != null) { - return fallbackUser; + Authenticator(String action, TransportMessage message, User fallbackUser) { + this.request = new Transport(action, message); + this.fallbackUser = fallbackUser; + } + + Authentication authenticate() throws IOException, IllegalArgumentException { + Authentication existing = getCurrentAuthentication(); + if (existing != null) { + return existing; } - if (AnonymousUser.enabled()) { - return AnonymousUser.INSTANCE; + + AuthenticationToken token = extractToken(); + if (token == null) { + return handleNullToken(); + } + + User user = authenticateToken(token); + if (user == null) { + throw handleNullUser(token); + } + user = lookupRunAsUserIfNecessary(user, token); + + final Authentication authentication = new Authentication(user, authenticatedBy, lookedupBy); + authentication.writeToContext(threadContext, cryptoService, signUserHeader); + return authentication; + } + + Authentication getCurrentAuthentication() { + Authentication authentication; + try { + authentication = Authentication.readFromContext(threadContext, cryptoService, signUserHeader); + } catch (Exception e) { + throw request.tamperedRequest(); + } + + // make sure this isn't a rest request since we don't allow authentication to be read via a HTTP request... + if (authentication != null && request instanceof Rest) { + throw request.tamperedRequest(); + } + return authentication; + } + + AuthenticationToken extractToken() { + AuthenticationToken token = null; + try { + for (Realm realm : realms) { + token = realm.token(threadContext); + if (token != null) { + logger.trace("realm [{}] resolved authentication token [{}] from [{}]", realm, token.principal(), request); + break; + } + } + } catch (Exception e) { + if (logger.isDebugEnabled()) { + logger.debug("failed to extract token from request: [{}]", e, request); + } else { + logger.warn("failed to extract token from request: [{}]", request, e.getMessage()); + } + throw request.exceptionProcessingRequest(e, null); + } + return token; + } + + Authentication handleNullToken() throws IOException { + Authentication authentication = null; + if (fallbackUser != null) { + RealmRef authenticatedBy = new RealmRef("__fallback", "__fallback", nodeName); + authentication = new Authentication(fallbackUser, authenticatedBy, null); + } else if (AnonymousUser.enabled()) { + RealmRef authenticatedBy = new RealmRef("__anonymous", "__anonymous", nodeName); + authentication = new Authentication(AnonymousUser.INSTANCE, authenticatedBy, null); + } + + if (authentication != null) { + authentication.writeToContext(threadContext, cryptoService, signUserHeader); + return authentication; } throw request.anonymousAccessDenied(); } - User user; - try { - user = authenticate(request, token); - } catch (Exception e) { - if (logger.isDebugEnabled()) { + User authenticateToken(AuthenticationToken token) { + User user = null; + try { + for (Realm realm : realms) { + if (realm.supports(token)) { + user = realm.authenticate(token); + if (user != null) { + authenticatedBy = new RealmRef(realm.name(), realm.type(), nodeName); + break; + } + request.realmAuthenticationFailed(token, realm.name()); + } + } + } catch (Exception e) { logger.debug("authentication failed for principal [{}], [{}]", e, request); + throw request.exceptionProcessingRequest(e, token); + } finally { + token.clearCredentials(); } - throw request.exceptionProcessingRequest(e, token); + return user; } - if (user == null) { - throw request.failedAuthentication(token); + ElasticsearchSecurityException handleNullUser(AuthenticationToken token) { + throw request.authenticationFailed(token); } - if (runAsEnabled) { + boolean shouldTryToRunAs(User authenticatedUser, AuthenticationToken token) { + if (runAsEnabled == false) { + return false; + } + String runAsUsername = threadContext.getHeader(RUN_AS_USER_HEADER); - if (runAsUsername != null) { - if (runAsUsername.isEmpty()) { - logger.warn("user [{}] attempted to runAs with an empty username", user.principal()); - throw request.failedAuthentication(token); - } - User runAsUser; - try { - runAsUser = lookupUser(runAsUsername); - } catch (Exception e) { - if (logger.isDebugEnabled()) { - logger.debug("lookup of run as user failed for principal [{}], [{}], run as username [{}]", e, - token.principal(), request, runAsUsername); + if (runAsUsername == null) { + return false; + } + + if (runAsUsername.isEmpty()) { + logger.debug("user [{}] attempted to runAs with an empty username", authenticatedUser.principal()); + throw request.runAsDenied(new User(authenticatedUser.principal(), authenticatedUser.roles(), + new User(runAsUsername, Strings.EMPTY_ARRAY)), token); + } + return true; + } + + User lookupRunAsUserIfNecessary(User authenticatedUser, AuthenticationToken token) { + User user = authenticatedUser; + if (shouldTryToRunAs(user, token) == false) { + return user; + } + + final String runAsUsername = threadContext.getHeader(RUN_AS_USER_HEADER); + try { + for (Realm realm : realms) { + if (realm.userLookupSupported()) { + User runAsUser = realm.lookupUser(runAsUsername); + if (runAsUser != null) { + lookedupBy = new RealmRef(realm.name(), realm.type(), nodeName); + user = new User(user.principal(), user.roles(), runAsUser); + return user; + } } - throw request.exceptionProcessingRequest(e, token); } - // wrap in a try catch because the user constructor could throw an exception if we are trying to runAs the system user - try { - if (runAsUser != null) { - user = new User(user.principal(), user.roles(), runAsUser); - } else { - // the requested run as user does not exist, but we don't throw an error here otherwise this could let - // information leak about users in the system... instead we'll just let the authz service fail throw an - // authorization error - user = new User(user.principal(), user.roles(), new User(runAsUsername, Strings.EMPTY_ARRAY)); - } - } catch (Exception e) { - if (logger.isDebugEnabled()) { - logger.debug("user creation failed for principal [{}], [{}], run as username [{}]", e, token.principal(), - request, runAsUsername); - } - throw request.exceptionProcessingRequest(e, token); - } + // the requested run as user does not exist, but we don't throw an error here otherwise this could let + // information leak about users in the system... instead we'll just let the authz service fail throw an + // authorization error + user = new User(user.principal(), user.roles(), new User(runAsUsername, Strings.EMPTY_ARRAY)); + } catch (Exception e) { + logger.debug("run as failed for principal [{}], [{}], run as username [{}]", e, token.principal(), request, runAsUsername); + throw request.exceptionProcessingRequest(e, token); } + return user; } - return user; - } - User authenticate(AuditableRequest request, AuthenticationToken token) throws ElasticsearchSecurityException { - assert token != null : "cannot authenticate null tokens"; - try { - for (Realm realm : realms) { - if (realm.supports(token)) { - User user = realm.authenticate(token); - if (user != null) { - return user; - } - request.authenticationFailed(token, realm.name()); - } - } - request.authenticationFailed(token); - return null; - } finally { - token.clearCredentials(); - } - } + abstract class AuditableRequest { - AuthenticationToken token(AuditableRequest request) throws ElasticsearchSecurityException { - for (Realm realm : realms) { - AuthenticationToken token = realm.token(threadContext); - if (token != null) { - request.tokenResolved(realm.name(), token); - return token; - } - } - return null; - } + abstract void realmAuthenticationFailed(AuthenticationToken token, String realm); - User lookupUser(String username) { - for (Realm realm : realms) { - if (realm.userLookupSupported()) { - User user = realm.lookupUser(username); - if (user != null) { - return user; + abstract ElasticsearchSecurityException tamperedRequest(); + + abstract ElasticsearchSecurityException exceptionProcessingRequest(Exception e, @Nullable AuthenticationToken token); + + abstract ElasticsearchSecurityException authenticationFailed(AuthenticationToken token); + + abstract ElasticsearchSecurityException anonymousAccessDenied(); + + abstract ElasticsearchSecurityException runAsDenied(User user, AuthenticationToken token); + } + + class Transport extends AuditableRequest { + + private final String action; + private final TransportMessage message; + + Transport(String action, TransportMessage message) { + this.action = action; + this.message = message; + } + + @Override + void realmAuthenticationFailed(AuthenticationToken token, String realm) { + auditTrail.authenticationFailed(realm, token, action, message); + } + + @Override + ElasticsearchSecurityException tamperedRequest() { + auditTrail.tamperedRequest(action, message); + return new ElasticsearchSecurityException("failed to verify signed authentication information"); + } + + @Override + ElasticsearchSecurityException exceptionProcessingRequest(Exception e, @Nullable AuthenticationToken token) { + if (token != null) { + auditTrail.authenticationFailed(token, action, message); + } else { + auditTrail.authenticationFailed(action, message); } + return failureHandler.exceptionProcessingRequest(message, action, e, threadContext); + } + + @Override + ElasticsearchSecurityException authenticationFailed(AuthenticationToken token) { + auditTrail.authenticationFailed(token, action, message); + return failureHandler.failedAuthentication(message, token, action, threadContext); + } + + @Override + ElasticsearchSecurityException anonymousAccessDenied() { + auditTrail.anonymousAccessDenied(action, message); + return failureHandler.missingToken(message, action, threadContext); + } + + @Override + ElasticsearchSecurityException runAsDenied(User user, AuthenticationToken token) { + auditTrail.runAsDenied(user, action, message); + return failureHandler.failedAuthentication(message, token, action, threadContext); + } + + public String toString() { + return "transport request action [" + action + "]"; + } + } + + class Rest extends AuditableRequest { + + private final RestRequest request; + + Rest(RestRequest request) { + this.request = request; + } + + @Override + void realmAuthenticationFailed(AuthenticationToken token, String realm) { + auditTrail.authenticationFailed(realm, token, request); + } + + @Override + ElasticsearchSecurityException tamperedRequest() { + auditTrail.tamperedRequest(request); + return new ElasticsearchSecurityException("rest request attempted to inject a user"); + } + + @Override + ElasticsearchSecurityException exceptionProcessingRequest(Exception e, @Nullable AuthenticationToken token) { + if (token != null) { + auditTrail.authenticationFailed(token, request); + } else { + auditTrail.authenticationFailed(request); + } + return failureHandler.exceptionProcessingRequest(request, e, threadContext); + } + + @Override + ElasticsearchSecurityException authenticationFailed(AuthenticationToken token) { + auditTrail.authenticationFailed(token, request); + return failureHandler.failedAuthentication(request, token, threadContext); + } + + @Override + ElasticsearchSecurityException anonymousAccessDenied() { + auditTrail.anonymousAccessDenied(request); + return failureHandler.missingToken(request, threadContext); + } + + @Override + ElasticsearchSecurityException runAsDenied(User user, AuthenticationToken token) { + auditTrail.runAsDenied(user, request); + return failureHandler.failedAuthentication(request, token, threadContext); + } + + public String toString() { + return "rest request uri [" + request.uri() + "]"; } } - return null; } public static void addSettings(List> settings) { settings.add(SIGN_USER_HEADER); settings.add(RUN_AS_ENABLED); } - - // these methods are package private for testing. They are also needed so that a AuditableRequest can be created in tests - AuditableRequest newRequest(String action, TransportMessage message) { - return new Transport(action, message); - } - - AuditableRequest newRequest(RestRequest request) { - return new Rest(request); - } - - abstract class AuditableRequest { - - abstract void authenticationFailed(AuthenticationToken token); - - abstract void authenticationFailed(AuthenticationToken token, String realm); - - abstract void tamperedRequest(); - - abstract void tokenResolved(String realm, AuthenticationToken token); - - abstract ElasticsearchSecurityException exceptionProcessingRequest(Exception e); - - abstract ElasticsearchSecurityException exceptionProcessingRequest(Exception e, AuthenticationToken token); - - abstract ElasticsearchSecurityException failedAuthentication(AuthenticationToken token); - - abstract ElasticsearchSecurityException anonymousAccessDenied(); - } - - class Transport extends AuditableRequest { - - private final String action; - private final TransportMessage message; - - Transport(String action, TransportMessage message) { - this.action = action; - this.message = message; - } - - @Override - void authenticationFailed(AuthenticationToken token) { - auditTrail.authenticationFailed(token, action, message); - } - - @Override - void authenticationFailed(AuthenticationToken token, String realm) { - auditTrail.authenticationFailed(realm, token, action, message); - } - - @Override - void tamperedRequest() { - auditTrail.tamperedRequest(action, message); - } - - @Override - void tokenResolved(String realm, AuthenticationToken token) { - logger.trace("realm [{}] resolved authentication token [{}] from transport request with action [{}]", - realm, token.principal(), action); - } - - @Override - ElasticsearchSecurityException exceptionProcessingRequest(Exception e) { - auditTrail.authenticationFailed(action, message); - return failureHandler.exceptionProcessingRequest(message, action, e, threadContext); - } - - @Override - ElasticsearchSecurityException exceptionProcessingRequest(Exception e, AuthenticationToken token) { - authenticationFailed(token); - return failureHandler.exceptionProcessingRequest(message, action, e, threadContext); - } - - ElasticsearchSecurityException failedAuthentication(AuthenticationToken token) { - auditTrail.authenticationFailed(token, action, message); - return failureHandler.failedAuthentication(message, token, action, threadContext); - } - - @Override - ElasticsearchSecurityException anonymousAccessDenied() { - auditTrail.anonymousAccessDenied(action, message); - return failureHandler.missingToken(message, action, threadContext); - } - - public String toString() { - return "transport action [" + action + "]"; - } - } - - class Rest extends AuditableRequest { - - private final RestRequest request; - - Rest(RestRequest request) { - this.request = request; - } - - @Override - void authenticationFailed(AuthenticationToken token) { - auditTrail.authenticationFailed(token, request); - } - - @Override - void authenticationFailed(AuthenticationToken token, String realm) { - auditTrail.authenticationFailed(realm, token, request); - } - - @Override - void tamperedRequest() { - auditTrail.tamperedRequest(request); - } - - @Override - void tokenResolved(String realm, AuthenticationToken token) { - logger.trace("realm [{}] resolved authentication token [{}] from rest request with uri [{}]", - realm, token.principal(), request.uri()); - } - - @Override - ElasticsearchSecurityException exceptionProcessingRequest(Exception e) { - auditTrail.authenticationFailed(request); - return failureHandler.exceptionProcessingRequest(request, e, threadContext); - } - - @Override - ElasticsearchSecurityException exceptionProcessingRequest(Exception e, AuthenticationToken token) { - authenticationFailed(token); - return failureHandler.exceptionProcessingRequest(request, e, threadContext); - } - - ElasticsearchSecurityException failedAuthentication(AuthenticationToken token) { - auditTrail.authenticationFailed(token, request); - return failureHandler.failedAuthentication(request, token, threadContext); - } - - @Override - ElasticsearchSecurityException anonymousAccessDenied() { - auditTrail.anonymousAccessDenied(request); - return failureHandler.missingToken(request, threadContext); - } - - public String toString() { - return "rest uri [" + request.uri() + "]"; - } - } } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/AuthorizationService.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/AuthorizationService.java index e914791de99..0804b9b658e 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/AuthorizationService.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/AuthorizationService.java @@ -6,6 +6,7 @@ package org.elasticsearch.shield.authz; import org.elasticsearch.ElasticsearchSecurityException; +import org.elasticsearch.shield.authc.Authentication; import org.elasticsearch.shield.user.User; import org.elasticsearch.transport.TransportRequest; @@ -29,11 +30,11 @@ public interface AuthorizationService { * have the appropriate privileges for this action/request, an {@link ElasticsearchSecurityException} * will be thrown. * - * @param user The user - * @param action The action - * @param request The request + * @param authentication The authentication information + * @param action The action + * @param request The request * @throws ElasticsearchSecurityException If the given user is no allowed to execute the given request */ - void authorize(User user, String action, TransportRequest request) throws ElasticsearchSecurityException; + void authorize(Authentication authentication, String action, TransportRequest request) throws ElasticsearchSecurityException; } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/AuthorizationUtils.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/AuthorizationUtils.java index f8db520203a..cbd67e982eb 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/AuthorizationUtils.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/AuthorizationUtils.java @@ -6,6 +6,7 @@ package org.elasticsearch.shield.authz; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.shield.authc.Authentication; import org.elasticsearch.shield.user.SystemUser; import org.elasticsearch.shield.user.User; import org.elasticsearch.shield.authc.InternalAuthenticationService; @@ -44,8 +45,8 @@ public final class AuthorizationUtils { return false; } - User user = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - if (user == null || SystemUser.is(user)) { + Authentication authentication = threadContext.getTransient(Authentication.AUTHENTICATION_KEY); + if (authentication == null || SystemUser.is(authentication.getUser())) { return true; } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java index ed8e4835333..a01029475e1 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java @@ -23,11 +23,11 @@ import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting.Property; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.common.settings.SettingsModule; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.search.action.SearchTransportService; import org.elasticsearch.shield.ShieldTemplateService; +import org.elasticsearch.shield.authc.Authentication; import org.elasticsearch.shield.user.AnonymousUser; import org.elasticsearch.shield.user.SystemUser; import org.elasticsearch.shield.user.User; @@ -140,32 +140,32 @@ public class InternalAuthorizationService extends AbstractComponent implements A } @Override - public void authorize(User user, String action, TransportRequest request) throws ElasticsearchSecurityException { + public void authorize(Authentication authentication, String action, TransportRequest request) throws ElasticsearchSecurityException { // prior to doing any authorization lets set the originating action in the context only setOriginatingAction(action); - User effectiveUser = user; // first we need to check if the user is the system. If it is, we'll just authorize the system access - if (SystemUser.is(user)) { - if (SystemUser.isAuthorized(action)) { + if (SystemUser.is(authentication.getRunAsUser())) { + if (SystemUser.isAuthorized(action) && SystemUser.is(authentication.getUser())) { setIndicesAccessControl(IndicesAccessControl.ALLOW_ALL); - grant(user, action, request); + grant(authentication, action, request); return; } - throw denial(user, action, request); + throw denial(authentication, action, request); } - GlobalPermission permission = permission(user.roles()); + // get the roles of the authenticated user, which may be different than the effective + GlobalPermission permission = permission(authentication.getUser().roles()); - final boolean isRunAs = user.runAs() != null; + final boolean isRunAs = authentication.getUser() != authentication.getRunAsUser(); // permission can be null as it might be that the user's role // is unknown if (permission == null || permission.isEmpty()) { if (isRunAs) { // the request is a run as request so we should call the specific audit event for a denied run as attempt - throw denyRunAs(user, action, request); + throw denyRunAs(authentication, action, request); } else { - throw denial(user, action, request); + throw denial(authentication, action, request); } } @@ -173,18 +173,17 @@ public class InternalAuthorizationService extends AbstractComponent implements A if (isRunAs) { // first we must authorize for the RUN_AS action RunAsPermission runAs = permission.runAs(); - if (runAs != null && runAs.check(user.runAs().principal())) { - grantRunAs(user, action, request); - permission = permission(user.runAs().roles()); + if (runAs != null && runAs.check(authentication.getRunAsUser().principal())) { + grantRunAs(authentication, action, request); + permission = permission(authentication.getRunAsUser().roles()); // permission can be null as it might be that the user's role // is unknown if (permission == null || permission.isEmpty()) { - throw denial(user, action, request); + throw denial(authentication, action, request); } - effectiveUser = user.runAs(); } else { - throw denyRunAs(user, action, request); + throw denyRunAs(authentication, action, request); } } @@ -193,17 +192,17 @@ public class InternalAuthorizationService extends AbstractComponent implements A if (ClusterPrivilege.ACTION_MATCHER.test(action)) { ClusterPermission cluster = permission.cluster(); // we use the effectiveUser for permission checking since we are running as a user! - if (cluster != null && cluster.check(action, request, effectiveUser)) { + if (cluster != null && cluster.check(action, request, authentication)) { setIndicesAccessControl(IndicesAccessControl.ALLOW_ALL); - grant(user, action, request); + grant(authentication, action, request); return; } - throw denial(user, action, request); + throw denial(authentication, action, request); } // ok... this is not a cluster action, let's verify it's an indices action if (!IndexPrivilege.ACTION_MATCHER.test(action)) { - throw denial(user, action, request); + throw denial(authentication, action, request); } // some APIs are indices requests that are not actually associated with indices. For example, @@ -217,33 +216,34 @@ public class InternalAuthorizationService extends AbstractComponent implements A //note that clear scroll shard level actions can originate from a clear scroll all, which doesn't require any //indices permission as it's categorized under cluster. This is why the scroll check is performed //even before checking if the user has any indices permission. - grant(user, action, request); + grant(authentication, action, request); return; } assert false : "only scroll related requests are known indices api that don't support retrieving the indices they relate to"; - throw denial(user, action, request); + throw denial(authentication, action, request); } if (permission.indices() == null || permission.indices().isEmpty()) { - throw denial(user, action, request); + throw denial(authentication, action, request); } ClusterState clusterState = clusterService.state(); - Set indexNames = resolveIndices(user, action, request, clusterState); + Set indexNames = resolveIndices(authentication, action, request, clusterState); assert !indexNames.isEmpty() : "every indices request needs to have its indices set thus the resolved indices must not be empty"; MetaData metaData = clusterState.metaData(); IndicesAccessControl indicesAccessControl = permission.authorize(action, indexNames, metaData); if (!indicesAccessControl.isGranted()) { - throw denial(user, action, request); + throw denial(authentication, action, request); } else if (indicesAccessControl.getIndexPermissions(ShieldTemplateService.SECURITY_INDEX_NAME) != null && indicesAccessControl.getIndexPermissions(ShieldTemplateService.SECURITY_INDEX_NAME).isGranted() - && XPackUser.is(user) == false + && XPackUser.is(authentication.getRunAsUser()) == false && MONITOR_INDEX_PREDICATE.test(action) == false) { // only the XPackUser is allowed to work with this index, but we should allow indices monitoring actions through for debugging // purposes. These monitor requests also sometimes resolve indices concretely and then requests them - logger.debug("user [{}] attempted to directly perform [{}] against the security index [{}]", user.principal(), action, - ShieldTemplateService.SECURITY_INDEX_NAME); - throw denial(user, action, request); + // FIXME its not just the XPackUser. We said the elastic user and superusers could access this! + logger.debug("user [{}] attempted to directly perform [{}] against the security index [{}]", + authentication.getRunAsUser().principal(), action, ShieldTemplateService.SECURITY_INDEX_NAME); + throw denial(authentication, action, request); } else { setIndicesAccessControl(indicesAccessControl); } @@ -259,14 +259,14 @@ public class InternalAuthorizationService extends AbstractComponent implements A } indicesAccessControl = permission.authorize("indices:admin/aliases", aliasesAndIndices, metaData); if (!indicesAccessControl.isGranted()) { - throw denial(user, "indices:admin/aliases", request); + throw denial(authentication, "indices:admin/aliases", request); } // no need to re-add the indicesAccessControl in the context, // because the create index call doesn't do anything FLS or DLS } } - grant(user, action, request); + grant(authentication, action, request); } private void setIndicesAccessControl(IndicesAccessControl accessControl) { @@ -304,15 +304,15 @@ public class InternalAuthorizationService extends AbstractComponent implements A return roles.build(); } - private Set resolveIndices(User user, String action, TransportRequest request, ClusterState clusterState) { + private Set resolveIndices(Authentication authentication, String action, TransportRequest request, ClusterState clusterState) { MetaData metaData = clusterState.metaData(); for (IndicesAndAliasesResolver resolver : indicesAndAliasesResolvers) { if (resolver.requestType().isInstance(request)) { - return resolver.resolve(user, action, request, metaData); + return resolver.resolve(authentication.getRunAsUser(), action, request, metaData); } } assert false : "we should be able to resolve indices for any known request that requires indices privileges"; - throw denial(user, action, request); + throw denial(authentication, action, request); } private static boolean isScrollRelatedAction(String action) { @@ -325,34 +325,36 @@ public class InternalAuthorizationService extends AbstractComponent implements A action.equals(SearchTransportService.CLEAR_SCROLL_CONTEXTS_ACTION_NAME); } - private ElasticsearchSecurityException denial(User user, String action, TransportRequest request) { - auditTrail.accessDenied(user, action, request); - return denialException(user, action); + private ElasticsearchSecurityException denial(Authentication authentication, String action, TransportRequest request) { + auditTrail.accessDenied(authentication.getUser(), action, request); + return denialException(authentication, action); } - private ElasticsearchSecurityException denyRunAs(User user, String action, TransportRequest request) { - auditTrail.runAsDenied(user, action, request); - return denialException(user, action); + private ElasticsearchSecurityException denyRunAs(Authentication authentication, String action, TransportRequest request) { + auditTrail.runAsDenied(authentication.getUser(), action, request); + return denialException(authentication, action); } - private void grant(User user, String action, TransportRequest request) { - auditTrail.accessGranted(user, action, request); + private void grant(Authentication authentication, String action, TransportRequest request) { + auditTrail.accessGranted(authentication.getUser(), action, request); } - private void grantRunAs(User user, String action, TransportRequest request) { - auditTrail.runAsGranted(user, action, request); + private void grantRunAs(Authentication authentication, String action, TransportRequest request) { + auditTrail.runAsGranted(authentication.getUser(), action, request); } - private ElasticsearchSecurityException denialException(User user, String action) { + private ElasticsearchSecurityException denialException(Authentication authentication, String action) { + final User user = authentication.getUser(); // Special case for anonymous user if (AnonymousUser.enabled() && AnonymousUser.is(user)) { if (anonymousAuthzExceptionEnabled == false) { throw authcFailureHandler.authenticationRequired(action, threadContext); } } - if (user.runAs() != null) { + // check for run as + if (user != authentication.getRunAsUser()) { return authorizationError("action [{}] is unauthorized for user [{}] run as [{}]", action, user.principal(), - user.runAs().principal()); + authentication.getRunAsUser().principal()); } return authorizationError("action [{}] is unauthorized for user [{}]", action, user.principal()); } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/ClusterPermission.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/ClusterPermission.java index 91370ba9f45..e9f8b715642 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/ClusterPermission.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/ClusterPermission.java @@ -5,8 +5,8 @@ */ package org.elasticsearch.shield.authz.permission; +import org.elasticsearch.shield.authc.Authentication; import org.elasticsearch.shield.authz.privilege.ClusterPrivilege; -import org.elasticsearch.shield.user.User; import org.elasticsearch.transport.TransportRequest; import java.util.List; @@ -17,13 +17,13 @@ import java.util.function.Predicate; */ public interface ClusterPermission extends Permission { - boolean check(String action, TransportRequest request, User user); + boolean check(String action, TransportRequest request, Authentication authentication); public static class Core implements ClusterPermission { public static final Core NONE = new Core(ClusterPrivilege.NONE) { @Override - public boolean check(String action, TransportRequest request, User user) { + public boolean check(String action, TransportRequest request, Authentication authentication) { return false; } @@ -46,7 +46,7 @@ public interface ClusterPermission extends Permission { } @Override - public boolean check(String action, TransportRequest request, User user) { + public boolean check(String action, TransportRequest request, Authentication authentication) { return predicate.test(action); } @@ -65,7 +65,7 @@ public interface ClusterPermission extends Permission { } @Override - public boolean check(String action, TransportRequest request, User user) { + public boolean check(String action, TransportRequest request, Authentication authentication) { if (globals == null) { return false; } @@ -73,7 +73,7 @@ public interface ClusterPermission extends Permission { if (global == null || global.cluster() == null) { throw new RuntimeException(); } - if (global.cluster().check(action, request, user)) { + if (global.cluster().check(action, request, authentication)) { return true; } } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/DefaultRole.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/DefaultRole.java index 80bf9874a06..b8e495b1cf2 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/DefaultRole.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/DefaultRole.java @@ -5,7 +5,9 @@ */ package org.elasticsearch.shield.authz.permission; -import org.elasticsearch.shield.user.User; +import org.elasticsearch.shield.authc.Authentication; +import org.elasticsearch.shield.authc.esnative.NativeRealm; +import org.elasticsearch.shield.authc.esnative.ReservedRealm; import org.elasticsearch.shield.action.user.AuthenticateAction; import org.elasticsearch.shield.action.user.ChangePasswordAction; import org.elasticsearch.shield.action.user.UserRequest; @@ -40,18 +42,47 @@ public class DefaultRole extends Role { } @Override - public boolean check(String action, TransportRequest request, User user) { - final boolean actionAllowed = super.check(action, request, user); + public boolean check(String action, TransportRequest request, Authentication authentication) { + final boolean actionAllowed = super.check(action, request, authentication); if (actionAllowed) { - assert request instanceof UserRequest; + if (request instanceof UserRequest == false) { + assert false : "right now only a user request should be allowed"; + return false; + } UserRequest userRequest = (UserRequest) request; String[] usernames = userRequest.usernames(); - assert usernames != null && usernames.length == 1; + if (usernames == null || usernames.length != 1 || usernames[0] == null) { + assert false : "this role should only be used for actions to apply to a single user"; + return false; + } final String username = usernames[0]; - assert username != null; - return user.principal().equals(username); + final boolean sameUsername = authentication.getRunAsUser().principal().equals(username); + if (sameUsername && ChangePasswordAction.NAME.equals(action)) { + return checkChangePasswordAction(authentication); + } + + assert AuthenticateAction.NAME.equals(action) || sameUsername == false; + return sameUsername; } return false; } } + + static boolean checkChangePasswordAction(Authentication authentication) { + // we need to verify that this user was authenticated by or looked up by a realm type that support password changes + // otherwise we open ourselves up to issues where a user in a different realm could be created with the same username + // and do malicious things + final boolean isRunAs = authentication.getUser() != authentication.getRunAsUser(); + final String realmType; + if (isRunAs) { + realmType = authentication.getLookedUpBy().getType(); + } else { + realmType = authentication.getAuthenticatedBy().getType(); + } + + assert realmType != null; + // ensure the user was authenticated by a realm that we can change a password for. The native realm is an internal realm and right + // now only one can exist in the realm configuration - if this changes we should update this check + return ReservedRealm.TYPE.equals(realmType) || NativeRealm.TYPE.equals(realmType); + } } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/transport/ClientTransportFilter.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/transport/ClientTransportFilter.java index 540319bfbca..a3d6a0006b1 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/transport/ClientTransportFilter.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/transport/ClientTransportFilter.java @@ -56,7 +56,7 @@ public interface ClientTransportFilter { the system user will be attached. There cannot be a request outgoing from this node that is not associated with a user. */ - authcService.attachUserHeaderIfMissing(SystemUser.INSTANCE); + authcService.attachUserIfMissing(SystemUser.INSTANCE); } } } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/transport/ServerTransportFilter.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/transport/ServerTransportFilter.java index eb22fa17821..4fa06c75a59 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/transport/ServerTransportFilter.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/transport/ServerTransportFilter.java @@ -8,7 +8,7 @@ package org.elasticsearch.shield.transport; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.util.concurrent.ThreadContext; -import org.elasticsearch.shield.user.User; +import org.elasticsearch.shield.authc.Authentication; import org.elasticsearch.shield.action.ShieldActionMapper; import org.elasticsearch.shield.authc.AuthenticationService; import org.elasticsearch.shield.authc.pki.PkiRealm; @@ -101,8 +101,8 @@ public interface ServerTransportFilter { } } - User user = authcService.authenticate(shieldAction, request, null); - authzService.authorize(user, shieldAction, request); + Authentication authentication = authcService.authenticate(shieldAction, request, null); + authzService.authorize(authentication, shieldAction, request); } } diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/action/filter/ShieldActionFilterTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/action/filter/ShieldActionFilterTests.java index 7dd5690a5f5..7bebea884c3 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/action/filter/ShieldActionFilterTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/action/filter/ShieldActionFilterTests.java @@ -12,7 +12,10 @@ import org.elasticsearch.action.search.SearchScrollRequest; import org.elasticsearch.action.support.ActionFilterChain; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; +import org.elasticsearch.shield.SecurityContext; import org.elasticsearch.shield.action.ShieldActionMapper; +import org.elasticsearch.shield.authc.Authentication; +import org.elasticsearch.shield.authc.Authentication.RealmRef; import org.elasticsearch.shield.user.SystemUser; import org.elasticsearch.shield.user.User; import org.elasticsearch.shield.audit.AuditTrail; @@ -62,7 +65,7 @@ public class ShieldActionFilterTests extends ESTestCase { ThreadPool threadPool = mock(ThreadPool.class); when(threadPool.getThreadContext()).thenReturn(new ThreadContext(Settings.EMPTY)); filter = new ShieldActionFilter(Settings.EMPTY, authcService, authzService, cryptoService, auditTrail, securityLicenseState, - new ShieldActionMapper(), new HashSet<>(), threadPool); + new ShieldActionMapper(), new HashSet<>(), threadPool, mock(SecurityContext.class)); } public void testApply() throws Exception { @@ -71,10 +74,11 @@ public class ShieldActionFilterTests extends ESTestCase { ActionFilterChain chain = mock(ActionFilterChain.class); Task task = mock(Task.class); User user = new User("username", "r1", "r2"); - when(authcService.authenticate("_action", request, SystemUser.INSTANCE)).thenReturn(user); + Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null); + when(authcService.authenticate("_action", request, SystemUser.INSTANCE)).thenReturn(authentication); doReturn(request).when(spy(filter)).unsign(user, "_action", request); filter.apply(task, "_action", request, listener, chain); - verify(authzService).authorize(user, "_action", request); + verify(authzService).authorize(authentication, "_action", request); verify(chain).proceed(eq(task), eq("_action"), eq(request), isA(ShieldActionFilter.SigningListener.class)); } @@ -85,8 +89,9 @@ public class ShieldActionFilterTests extends ESTestCase { RuntimeException exception = new RuntimeException("process-error"); Task task = mock(Task.class); User user = new User("username", "r1", "r2"); - when(authcService.authenticate("_action", request, SystemUser.INSTANCE)).thenReturn(user); - doThrow(exception).when(authzService).authorize(user, "_action", request); + Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null); + when(authcService.authenticate("_action", request, SystemUser.INSTANCE)).thenReturn(authentication); + doThrow(exception).when(authzService).authorize(authentication, "_action", request); filter.apply(task, "_action", request, listener, chain); verify(listener).onFailure(exception); verifyNoMoreInteractions(chain); @@ -98,12 +103,13 @@ public class ShieldActionFilterTests extends ESTestCase { ActionFilterChain chain = mock(ActionFilterChain.class); User user = mock(User.class); Task task = mock(Task.class); - when(authcService.authenticate("_action", request, SystemUser.INSTANCE)).thenReturn(user); + Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null); + when(authcService.authenticate("_action", request, SystemUser.INSTANCE)).thenReturn(authentication); when(cryptoService.signed("signed_scroll_id")).thenReturn(true); when(cryptoService.unsignAndVerify("signed_scroll_id")).thenReturn("scroll_id"); filter.apply(task, "_action", request, listener, chain); assertThat(request.scrollId(), equalTo("scroll_id")); - verify(authzService).authorize(user, "_action", request); + verify(authzService).authorize(authentication, "_action", request); verify(chain).proceed(eq(task), eq("_action"), eq(request), isA(ShieldActionFilter.SigningListener.class)); } @@ -114,7 +120,8 @@ public class ShieldActionFilterTests extends ESTestCase { IllegalArgumentException sigException = new IllegalArgumentException("bad bad boy"); User user = mock(User.class); Task task = mock(Task.class); - when(authcService.authenticate("_action", request, SystemUser.INSTANCE)).thenReturn(user); + Authentication authentication = new Authentication(user, new RealmRef("test", "test", "foo"), null); + when(authcService.authenticate("_action", request, SystemUser.INSTANCE)).thenReturn(authentication); when(cryptoService.signed("scroll_id")).thenReturn(true); doThrow(sigException).when(cryptoService).unsignAndVerify("scroll_id"); filter.apply(task, "_action", request, listener, chain); diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authc/InternalAuthenticationServiceTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authc/InternalAuthenticationServiceTests.java index c21233e739e..1869972c94e 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authc/InternalAuthenticationServiceTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authc/InternalAuthenticationServiceTests.java @@ -7,7 +7,6 @@ package org.elasticsearch.shield.authc; import org.elasticsearch.ElasticsearchException; import org.elasticsearch.ElasticsearchSecurityException; -import org.elasticsearch.Version; import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.settings.Settings; @@ -15,7 +14,8 @@ import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.env.Environment; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestRequest; -import org.elasticsearch.shield.authc.InternalAuthenticationService.AuditableRequest; +import org.elasticsearch.shield.authc.Authentication.RealmRef; +import org.elasticsearch.shield.authc.InternalAuthenticationService.Authenticator; import org.elasticsearch.shield.SecurityLicenseState.EnabledRealmType; import org.elasticsearch.shield.user.AnonymousUser; import org.elasticsearch.shield.user.SystemUser; @@ -32,17 +32,14 @@ import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.TransportMessage; import org.junit.After; import org.junit.Before; -import org.junit.Rule; -import org.junit.rules.ExpectedException; +import java.io.IOException; import java.util.Arrays; -import java.util.Base64; import java.util.Collections; import static org.elasticsearch.shield.support.Exceptions.authenticationError; import static org.elasticsearch.test.ShieldTestsUtils.assertAuthenticationException; import static org.hamcrest.Matchers.arrayContaining; -import static org.hamcrest.Matchers.arrayContainingInAnyOrder; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; @@ -50,11 +47,10 @@ import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; import static org.hamcrest.Matchers.sameInstance; import static org.mockito.Matchers.any; -import static org.mockito.Mockito.doReturn; +import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; @@ -66,9 +62,6 @@ import static org.mockito.Mockito.when; */ public class InternalAuthenticationServiceTests extends ESTestCase { - @Rule - public ExpectedException thrown = ExpectedException.none(); - InternalAuthenticationService service; TransportMessage message; RestRequest restRequest; @@ -88,10 +81,15 @@ public class InternalAuthenticationServiceTests extends ESTestCase { message = new InternalMessage(); restRequest = new FakeRestRequest(); firstRealm = mock(Realm.class); - when(firstRealm.name()).thenReturn("file"); + when(firstRealm.type()).thenReturn("file"); + when(firstRealm.name()).thenReturn("file_realm"); secondRealm = mock(Realm.class); - when(secondRealm.name()).thenReturn("second"); - Settings settings = Settings.builder().put("path.home", createTempDir()).build(); + when(secondRealm.type()).thenReturn("second"); + when(secondRealm.name()).thenReturn("second_realm"); + Settings settings = Settings.builder() + .put("path.home", createTempDir()) + .put("node.name", "authc_test") + .build(); SecurityLicenseState shieldLicenseState = mock(SecurityLicenseState.class); when(shieldLicenseState.enabledRealmType()).thenReturn(EnabledRealmType.ALL); when(shieldLicenseState.authenticationAndAuthorizationEnabled()).thenReturn(true); @@ -113,7 +111,8 @@ public class InternalAuthenticationServiceTests extends ESTestCase { threadContext = new ThreadContext(Settings.EMPTY); controller = mock(RestController.class); when(threadPool.getThreadContext()).thenReturn(threadContext); - service = new InternalAuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService, + when(cryptoService.sign(any(String.class))).thenReturn("_signed_auth"); + service = new InternalAuthenticationService(settings, realms, auditTrail, cryptoService, new DefaultAuthenticationFailureHandler(), threadPool, controller); } @@ -127,17 +126,21 @@ public class InternalAuthenticationServiceTests extends ESTestCase { when(firstRealm.token(threadContext)).thenReturn(null); when(secondRealm.token(threadContext)).thenReturn(token); - AuthenticationToken result = service.token(service.newRequest("_action", message)); + Authenticator authenticator = service.createAuthenticator("_action", message, null); + AuthenticationToken result = authenticator.extractToken(); assertThat(result, notNullValue()); assertThat(result, is(token)); verifyZeroInteractions(auditTrail); } public void testTokenMissing() throws Exception { - AuthenticationToken token = service.token(service.newRequest("_action", message)); + Authenticator authenticator = service.createAuthenticator("_action", message, null); + AuthenticationToken token = authenticator.extractToken(); assertThat(token, nullValue()); + ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class, authenticator::handleNullToken); + assertThat(e.getMessage(), containsString("missing authentication token")); + verify(auditTrail).anonymousAccessDenied("_action", message); verifyNoMoreInteractions(auditTrail); - assertThat(threadContext.getTransient(InternalAuthenticationService.TOKEN_KEY), nullValue()); } @SuppressWarnings("unchecked") @@ -147,20 +150,20 @@ public class InternalAuthenticationServiceTests extends ESTestCase { when(firstRealm.authenticate(token)).thenReturn(null); // first fails when(secondRealm.supports(token)).thenReturn(true); when(secondRealm.authenticate(token)).thenReturn(user); + if (randomBoolean()) { + when(firstRealm.token(threadContext)).thenReturn(token); + } else { + when(secondRealm.token(threadContext)).thenReturn(token); + } - service = spy(service); - doReturn(token).when(service).token(any(AuditableRequest.class)); - - when(cryptoService.sign(InternalAuthenticationService.encodeUser(user, null))).thenReturn("_encoded_user"); - - User result = service.authenticate("_action", message, null); + Authentication result = service.authenticate("_action", message, null); assertThat(result, notNullValue()); - assertThat(result, is(user)); - verify(auditTrail).authenticationFailed("file", token, "_action", message); - User user1 = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - assertThat(user1, notNullValue()); - assertThat(user1, sameInstance(user)); - assertThat(threadContext.getHeader(InternalAuthenticationService.USER_KEY), equalTo((Object) "_encoded_user")); + assertThat(result.getUser(), is(user)); + assertThat(result.getLookedUpBy(), is(nullValue())); + assertThat(result.getAuthenticatedBy(), is(notNullValue())); // TODO implement equals + verify(auditTrail).authenticationFailed(firstRealm.name(), token, "_action", message); + verify(cryptoService).sign(any(String.class)); + assertThreadContextContainsAuthentication(result); } public void testAuthenticateFirstNotSupportingSecondSucceeds() throws Exception { @@ -168,36 +171,28 @@ public class InternalAuthenticationServiceTests extends ESTestCase { when(firstRealm.supports(token)).thenReturn(false); when(secondRealm.supports(token)).thenReturn(true); when(secondRealm.authenticate(token)).thenReturn(user); + when(secondRealm.token(threadContext)).thenReturn(token); - service = spy(service); - doReturn(token).when(service).token(any(AuditableRequest.class)); - - when(cryptoService.sign(InternalAuthenticationService.encodeUser(user, null))).thenReturn("_encoded_user"); - - User result = service.authenticate("_action", message, null); + Authentication result = service.authenticate("_action", message, null); assertThat(result, notNullValue()); - assertThat(result, is(user)); + assertThat(result.getUser(), is(user)); verifyZeroInteractions(auditTrail); verify(firstRealm, never()).authenticate(token); - User user1 = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - assertThat(user1, notNullValue()); - assertThat(user1, is((Object) user)); - assertThat(threadContext.getHeader(InternalAuthenticationService.USER_KEY), equalTo((Object) "_encoded_user")); + assertThreadContextContainsAuthentication(result); } public void testAuthenticateCached() throws Exception { - User user = new User("_username", "r1"); - threadContext.putTransient(InternalAuthenticationService.USER_KEY, user); - User result = service.authenticate("_action", message, null); + final Authentication authentication = new Authentication(new User("_username", "r1"), new RealmRef("test", "cached", "foo"), null); + authentication.writeToContext(threadContext, cryptoService, true); + + Authentication result = service.authenticate("_action", message, null); + assertThat(result, notNullValue()); - assertThat(result, is(user)); + assertThat(result, is(authentication)); verifyZeroInteractions(auditTrail); verifyZeroInteractions(firstRealm); verifyZeroInteractions(secondRealm); - verifyZeroInteractions(cryptoService); - User user1 = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - assertThat(user1, notNullValue()); - assertThat(user1, is(user)); + verify(cryptoService).sign(any(String.class)); } public void testAuthenticateNonExistentRestRequestUserThrowsAuthenticationException() throws Exception { @@ -214,36 +209,30 @@ public class InternalAuthenticationServiceTests extends ESTestCase { public void testTokenRestMissing() throws Exception { when(firstRealm.token(threadContext)).thenReturn(null); when(secondRealm.token(threadContext)).thenReturn(null); - AuthenticationToken token = service.token(service.newRequest(restRequest)); + + Authenticator authenticator = service.createAuthenticator(restRequest); + AuthenticationToken token = authenticator.extractToken(); + assertThat(token, nullValue()); } - public void testEncodeDecodeUser() throws Exception { - User user = new User("username", "r1", "r2", "r3"); - String text = InternalAuthenticationService.encodeUser(user, null); - User user2 = InternalAuthenticationService.decodeUser(text); - assertThat(user, equalTo(user2)); - - text = InternalAuthenticationService.encodeUser(SystemUser.INSTANCE, null); - user2 = InternalAuthenticationService.decodeUser(text); - assertThat(SystemUser.INSTANCE, sameInstance(user2)); - } - - public void testUserHeader() throws Exception { + public void authenticationInContextAndHeader() throws Exception { User user = new User("_username", "r1"); when(firstRealm.token(threadContext)).thenReturn(token); when(firstRealm.supports(token)).thenReturn(true); when(firstRealm.authenticate(token)).thenReturn(user); - when(cryptoService.sign(InternalAuthenticationService.encodeUser(user, null))).thenReturn("_signed_user"); - service = spy(service); - AuditableRequest request = service.newRequest("_action", message); - doReturn(token).when(service).token(request); - User result = service.authenticate("_action", message, null); + + Authentication result = service.authenticate("_action", message, null); + assertThat(result, notNullValue()); - assertThat(result, is(user)); - String userStr = threadContext.getHeader(InternalAuthenticationService.USER_KEY); + assertThat(result.getUser(), is(user)); + + String userStr = threadContext.getHeader(Authentication.AUTHENTICATION_KEY); assertThat(userStr, notNullValue()); - assertThat(userStr, equalTo("_signed_user")); + assertThat(userStr, equalTo("_signed_auth")); + + Authentication ctxAuth = threadContext.getTransient(Authentication.AUTHENTICATION_KEY); + assertThat(ctxAuth, is(result)); } public void testAuthenticateTransportAnonymous() throws Exception { @@ -276,38 +265,24 @@ public class InternalAuthenticationServiceTests extends ESTestCase { when(firstRealm.token(threadContext)).thenReturn(null); when(secondRealm.token(threadContext)).thenReturn(null); User user1 = new User("username", "r1", "r2"); - when(cryptoService.sign(InternalAuthenticationService.encodeUser(user1, null))).thenReturn("_signed_user"); - User user2 = service.authenticate("_action", message, user1); - assertThat(user1, sameInstance(user2)); - User user3 = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - assertThat(user3, sameInstance(user2)); - assertThat(threadContext.getHeader(InternalAuthenticationService.USER_KEY), equalTo((Object) "_signed_user")); + + Authentication result = service.authenticate("_action", message, user1); + assertThat(result, notNullValue()); + assertThat(result.getUser(), sameInstance(user1)); + assertThreadContextContainsAuthentication(result); } - public void testAuthenticateTransportSuccessNoFallback() throws Exception { - User user1 = new User("username", "r1", "r2"); + public void testAuthenticateTransportSuccess() throws Exception { + User user = new User("username", "r1", "r2"); + User fallback = randomBoolean() ? SystemUser.INSTANCE : null; when(firstRealm.token(threadContext)).thenReturn(token); when(firstRealm.supports(token)).thenReturn(true); - when(firstRealm.authenticate(token)).thenReturn(user1); - when(cryptoService.sign(InternalAuthenticationService.encodeUser(user1, null))).thenReturn("_signed_user"); - User user2 = service.authenticate("_action", message, null); - assertThat(user1, sameInstance(user2)); - User user3 = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - assertThat(user3, sameInstance(user2)); - assertThat(threadContext.getHeader(InternalAuthenticationService.USER_KEY), equalTo("_signed_user")); - } + when(firstRealm.authenticate(token)).thenReturn(user); - public void testAuthenticateTransportSuccessWithFallback() throws Exception { - User user1 = new User("username", "r1", "r2"); - when(firstRealm.token(threadContext)).thenReturn(token); - when(firstRealm.supports(token)).thenReturn(true); - when(firstRealm.authenticate(token)).thenReturn(user1); - when(cryptoService.sign(InternalAuthenticationService.encodeUser(user1, null))).thenReturn("_signed_user"); - User user2 = service.authenticate("_action", message, SystemUser.INSTANCE); - assertThat(user1, sameInstance(user2)); - User user3 = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - assertThat(user3, sameInstance((Object) user2)); - assertThat(threadContext.getHeader(InternalAuthenticationService.USER_KEY), equalTo((Object) "_signed_user")); + Authentication result = service.authenticate("_action", message, fallback); + assertThat(result, notNullValue()); + assertThat(result.getUser(), sameInstance(user)); + assertThreadContextContainsAuthentication(result); } public void testAuthenticateRestSuccess() throws Exception { @@ -315,10 +290,10 @@ public class InternalAuthenticationServiceTests extends ESTestCase { when(firstRealm.token(threadContext)).thenReturn(token); when(firstRealm.supports(token)).thenReturn(true); when(firstRealm.authenticate(token)).thenReturn(user1); - User user2 = service.authenticate(restRequest); - assertThat(user1, sameInstance(user2)); - User user3 = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - assertThat(user3, sameInstance(user2)); + Authentication result = service.authenticate(restRequest); + assertThat(result, notNullValue()); + assertThat(result.getUser(), sameInstance(user1)); + assertThreadContextContainsAuthentication(result); } public void testAutheticateTransportContextAndHeader() throws Exception { @@ -326,12 +301,10 @@ public class InternalAuthenticationServiceTests extends ESTestCase { when(firstRealm.token(threadContext)).thenReturn(token); when(firstRealm.supports(token)).thenReturn(true); when(firstRealm.authenticate(token)).thenReturn(user1); - when(cryptoService.sign(InternalAuthenticationService.encodeUser(user1, null))).thenReturn("_signed_user"); - User user2 = service.authenticate("_action", message, SystemUser.INSTANCE); - assertThat(user1, sameInstance(user2)); - User user3 = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - assertThat(user3, sameInstance(user2)); - assertThat(threadContext.getHeader(InternalAuthenticationService.USER_KEY), equalTo((Object) "_signed_user")); + Authentication authentication = service.authenticate("_action", message, SystemUser.INSTANCE); + assertThat(authentication, notNullValue()); + assertThat(authentication.getUser(), sameInstance(user1)); + assertThreadContextContainsAuthentication(authentication); reset(firstRealm); // checking authentication from the context @@ -341,10 +314,10 @@ public class InternalAuthenticationServiceTests extends ESTestCase { service = new InternalAuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService, new DefaultAuthenticationFailureHandler(), threadPool, controller); - threadContext1.putTransient(InternalAuthenticationService.USER_KEY, - threadContext.getTransient(InternalAuthenticationService.USER_KEY)); - User user = service.authenticate("_action", message1, SystemUser.INSTANCE); - assertThat(user, sameInstance(user1)); + threadContext1.putTransient(Authentication.AUTHENTICATION_KEY, threadContext.getTransient(Authentication.AUTHENTICATION_KEY)); + threadContext1.putHeader(Authentication.AUTHENTICATION_KEY, threadContext.getHeader(Authentication.AUTHENTICATION_KEY)); + Authentication ctxAuth = service.authenticate("_action", message1, SystemUser.INSTANCE); + assertThat(ctxAuth, sameInstance(authentication)); verifyZeroInteractions(firstRealm); reset(firstRealm); @@ -354,8 +327,8 @@ public class InternalAuthenticationServiceTests extends ESTestCase { when(threadPool.getThreadContext()).thenReturn(threadContext1); service = new InternalAuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService, new DefaultAuthenticationFailureHandler(), threadPool, controller); - threadContext1.putHeader(InternalAuthenticationService.USER_KEY, threadContext.getHeader(InternalAuthenticationService.USER_KEY)); - when(cryptoService.unsignAndVerify("_signed_user")).thenReturn(InternalAuthenticationService.encodeUser(user1, null)); + threadContext1.putHeader(Authentication.AUTHENTICATION_KEY, threadContext.getHeader(Authentication.AUTHENTICATION_KEY)); + when(cryptoService.unsignAndVerify("_signed_auth")).thenReturn(authentication.encode()); BytesStreamOutput output = new BytesStreamOutput(); threadContext1.writeTo(output); @@ -366,12 +339,13 @@ public class InternalAuthenticationServiceTests extends ESTestCase { when(threadPool.getThreadContext()).thenReturn(threadContext1); service = new InternalAuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService, new DefaultAuthenticationFailureHandler(), threadPool, controller); - user = service.authenticate("_action", new InternalMessage(), SystemUser.INSTANCE); - assertThat(user, equalTo(user1)); + Authentication result = service.authenticate("_action", new InternalMessage(), SystemUser.INSTANCE); + assertThat(result, notNullValue()); + assertThat(result.getUser(), equalTo(user1)); verifyZeroInteractions(firstRealm); } - public void testAutheticateTransportContextAndHeaderNoSigning() throws Exception { + public void testAuthenticateTransportContextAndHeaderNoSigning() throws Exception { Settings settings = Settings.builder().put(InternalAuthenticationService.SIGN_USER_HEADER.getKey(), false).build(); service = new InternalAuthenticationService(settings, realms, auditTrail, cryptoService, new DefaultAuthenticationFailureHandler(), threadPool, controller); @@ -380,12 +354,10 @@ public class InternalAuthenticationServiceTests extends ESTestCase { when(firstRealm.supports(token)).thenReturn(true); when(firstRealm.token(threadContext)).thenReturn(token); when(firstRealm.authenticate(token)).thenReturn(user1); - User user2 = service.authenticate("_action", message, SystemUser.INSTANCE); - assertThat(user1, sameInstance(user2)); - User user3 = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - assertThat(user3, sameInstance(user2)); - assertThat(threadContext.getHeader(InternalAuthenticationService.USER_KEY), - equalTo((Object) InternalAuthenticationService.encodeUser(user1, null))); + Authentication authentication = service.authenticate("_action", message, SystemUser.INSTANCE); + assertThat(authentication, notNullValue()); + assertThat(authentication.getUser(), sameInstance(user1)); + assertThreadContextContainsAuthentication(authentication, false); reset(firstRealm); // checking authentication from the context @@ -394,17 +366,16 @@ public class InternalAuthenticationServiceTests extends ESTestCase { when(threadPool.getThreadContext()).thenReturn(threadContext1); service = new InternalAuthenticationService(Settings.EMPTY, realms, auditTrail, cryptoService, new DefaultAuthenticationFailureHandler(), threadPool, controller); - threadContext1.putTransient(InternalAuthenticationService.USER_KEY, - threadContext.getTransient(InternalAuthenticationService.USER_KEY)); - User user = service.authenticate("_action", message1, SystemUser.INSTANCE); - assertThat(user, sameInstance(user1)); + threadContext1.putTransient(Authentication.AUTHENTICATION_KEY, threadContext.getTransient(Authentication.AUTHENTICATION_KEY)); + threadContext1.putHeader(Authentication.AUTHENTICATION_KEY, threadContext.getHeader(Authentication.AUTHENTICATION_KEY)); + Authentication ctxAuth = service.authenticate("_action", message1, SystemUser.INSTANCE); + assertThat(ctxAuth, sameInstance(authentication)); verifyZeroInteractions(firstRealm); reset(firstRealm); - // checking authentication from the user header threadContext1 = new ThreadContext(Settings.EMPTY); - threadContext1.putHeader(InternalAuthenticationService.USER_KEY, threadContext.getHeader(InternalAuthenticationService.USER_KEY)); + threadContext1.putHeader(Authentication.AUTHENTICATION_KEY, threadContext.getHeader(Authentication.AUTHENTICATION_KEY)); BytesStreamOutput output = new BytesStreamOutput(); threadContext1.writeTo(output); @@ -415,8 +386,9 @@ public class InternalAuthenticationServiceTests extends ESTestCase { when(threadPool.getThreadContext()).thenReturn(threadContext1); service = new InternalAuthenticationService(settings, realms, auditTrail, cryptoService, new DefaultAuthenticationFailureHandler(), threadPool, controller); - user = service.authenticate("_action", new InternalMessage(), SystemUser.INSTANCE); - assertThat(user, equalTo(user1)); + Authentication result = service.authenticate("_action", new InternalMessage(), SystemUser.INSTANCE); + assertThat(result, notNullValue()); + assertThat(result.getUser(), equalTo(user1)); verifyZeroInteractions(firstRealm); verifyZeroInteractions(cryptoService); @@ -424,8 +396,8 @@ public class InternalAuthenticationServiceTests extends ESTestCase { public void testAuthenticateTamperedUser() throws Exception { InternalMessage message = new InternalMessage(); - threadContext.putHeader(InternalAuthenticationService.USER_KEY, "_signed_user"); - when(cryptoService.unsignAndVerify("_signed_user")).thenThrow( + threadContext.putHeader(Authentication.AUTHENTICATION_KEY, "_signed_auth"); + when(cryptoService.unsignAndVerify("_signed_auth")).thenThrow( randomFrom(new RuntimeException(), new IllegalArgumentException(), new IllegalStateException())); try { @@ -444,23 +416,26 @@ public class InternalAuthenticationServiceTests extends ESTestCase { } else { user = new User("username", "r1", "r2"); } - assertThat(threadContext.getTransient(InternalAuthenticationService.USER_KEY), nullValue()); - assertThat(threadContext.getHeader(InternalAuthenticationService.USER_KEY), nullValue()); - when(cryptoService.sign(InternalAuthenticationService.encodeUser(user, null))).thenReturn("_signed_user"); - service.attachUserHeaderIfMissing(user); - User user1 = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - assertThat(user1, sameInstance((Object) user)); - assertThat(threadContext.getHeader(InternalAuthenticationService.USER_KEY), equalTo((Object) "_signed_user")); + assertThat(threadContext.getTransient(Authentication.AUTHENTICATION_KEY), nullValue()); + assertThat(threadContext.getHeader(Authentication.AUTHENTICATION_KEY), nullValue()); + service.attachUserIfMissing(user); + + Authentication authentication = threadContext.getTransient(Authentication.AUTHENTICATION_KEY); + assertThat(authentication, notNullValue()); + assertThat(authentication.getUser(), sameInstance((Object) user)); + assertThat(authentication.getLookedUpBy(), nullValue()); + assertThat(authentication.getAuthenticatedBy().getName(), is("__attach")); + assertThat(authentication.getAuthenticatedBy().getType(), is("__attach")); + assertThat(authentication.getAuthenticatedBy().getNodeName(), is("authc_test")); + assertThat(threadContext.getHeader(Authentication.AUTHENTICATION_KEY), equalTo((Object) "_signed_auth")); } public void testAttachIfMissingExists() throws Exception { - User user = new User("username", "r1", "r2"); - threadContext.putTransient(InternalAuthenticationService.USER_KEY, user); - threadContext.putHeader(InternalAuthenticationService.USER_KEY, "_signed_user"); - service.attachUserHeaderIfMissing(new User("username2", "r3", "r4")); - User user1 = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - assertThat(user1, sameInstance(user)); - assertThat(threadContext.getHeader(InternalAuthenticationService.USER_KEY), equalTo("_signed_user")); + Authentication authentication = new Authentication(new User("username", "r1", "r2"), new RealmRef("test", "test", "foo"), null); + threadContext.putTransient(Authentication.AUTHENTICATION_KEY, authentication); + threadContext.putHeader(Authentication.AUTHENTICATION_KEY, "_signed_auth"); + service.attachUserIfMissing(new User("username2", "r3", "r4")); + assertThreadContextContainsAuthentication(authentication); } public void testAnonymousUserRest() throws Exception { @@ -474,16 +449,13 @@ public class InternalAuthenticationServiceTests extends ESTestCase { AnonymousUser.initialize(settings); service = new InternalAuthenticationService(settings, realms, auditTrail, cryptoService, new DefaultAuthenticationFailureHandler(), threadPool, controller); - RestRequest request = new FakeRestRequest(); - User user = service.authenticate(request); - User user1 = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - assertThat(user1, notNullValue()); - assertThat(user1, sameInstance((Object) user)); - assertThat(user, notNullValue()); - assertThat(user.principal(), equalTo(username)); - assertThat(user.roles(), arrayContainingInAnyOrder("r1", "r2", "r3")); + Authentication result = service.authenticate(request); + + assertThat(result, notNullValue()); + assertThat(result.getUser(), sameInstance((Object) AnonymousUser.INSTANCE)); + assertThreadContextContainsAuthentication(result); } public void testAnonymousUserTransportNoDefaultUser() throws Exception { @@ -493,13 +465,12 @@ public class InternalAuthenticationServiceTests extends ESTestCase { AnonymousUser.initialize(settings); service = new InternalAuthenticationService(settings, realms, auditTrail, cryptoService, new DefaultAuthenticationFailureHandler(), threadPool, controller); - InternalMessage message = new InternalMessage(); - User user = service.authenticate("_action", message, null); - assertThat(user, notNullValue()); - assertThat(user.principal(), equalTo(AnonymousUser.DEFAULT_ANONYMOUS_USERNAME)); - assertThat(user.roles(), arrayContainingInAnyOrder("r1", "r2", "r3")); + Authentication result = service.authenticate("_action", message, null); + assertThat(result, notNullValue()); + assertThat(result.getUser(), sameInstance(AnonymousUser.INSTANCE)); + assertThreadContextContainsAuthentication(result); } public void testAnonymousUserTransportWithDefaultUser() throws Exception { @@ -512,9 +483,10 @@ public class InternalAuthenticationServiceTests extends ESTestCase { InternalMessage message = new InternalMessage(); - User user = service.authenticate("_action", message, SystemUser.INSTANCE); - assertThat(user, notNullValue()); - assertThat(user, sameInstance(SystemUser.INSTANCE)); + Authentication result = service.authenticate("_action", message, SystemUser.INSTANCE); + assertThat(result, notNullValue()); + assertThat(result.getUser(), sameInstance(SystemUser.INSTANCE)); + assertThreadContextContainsAuthentication(result); } public void testRealmTokenThrowingException() throws Exception { @@ -638,7 +610,14 @@ public class InternalAuthenticationServiceTests extends ESTestCase { when(secondRealm.lookupUser("run_as")).thenReturn(new User("looked up user", new String[]{"some role"})); when(secondRealm.userLookupSupported()).thenReturn(true); - User authenticated = service.authenticate("_action", message, null); + Authentication result; + if (randomBoolean()) { + result = service.authenticate("_action", message, null); + } else { + result = service.authenticate(restRequest); + } + assertThat(result, notNullValue()); + User authenticated = result.getUser(); assertThat(SystemUser.is(authenticated), is(false)); assertThat(authenticated.runAs(), is(notNullValue())); @@ -646,29 +625,7 @@ public class InternalAuthenticationServiceTests extends ESTestCase { assertThat(authenticated.roles(), arrayContaining("user")); assertThat(authenticated.runAs().principal(), is("looked up user")); assertThat(authenticated.runAs().roles(), arrayContaining("some role")); - User user1 = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - assertThat(user1, sameInstance(authenticated)); - } - - public void testRunAsLookupSameRealmRest() throws Exception { - AuthenticationToken token = mock(AuthenticationToken.class); - threadContext.putHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, "run_as"); - when(secondRealm.token(threadContext)).thenReturn(token); - when(secondRealm.supports(token)).thenReturn(true); - when(secondRealm.authenticate(token)).thenReturn(new User("lookup user", new String[]{"user"})); - when(secondRealm.lookupUser("run_as")).thenReturn(new User("looked up user", new String[]{"some role"})); - when(secondRealm.userLookupSupported()).thenReturn(true); - - User authenticated = service.authenticate(restRequest); - - assertThat(SystemUser.is(authenticated), is(false)); - assertThat(authenticated.runAs(), is(notNullValue())); - assertThat(authenticated.principal(), is("lookup user")); - assertThat(authenticated.roles(), arrayContaining("user")); - assertThat(authenticated.runAs().principal(), is("looked up user")); - assertThat(authenticated.runAs().roles(), arrayContaining("some role")); - User user1 = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - assertThat(user1, sameInstance(authenticated)); + assertThreadContextContainsAuthentication(result); } public void testRunAsLookupDifferentRealm() throws Exception { @@ -681,7 +638,14 @@ public class InternalAuthenticationServiceTests extends ESTestCase { when(firstRealm.lookupUser("run_as")).thenReturn(new User("looked up user", new String[]{"some role"})); when(firstRealm.userLookupSupported()).thenReturn(true); - User authenticated = service.authenticate("_action", message, null); + Authentication result; + if (randomBoolean()) { + result = service.authenticate("_action", message, null); + } else { + result = service.authenticate(restRequest); + } + assertThat(result, notNullValue()); + User authenticated = result.getUser(); assertThat(SystemUser.is(authenticated), is(false)); assertThat(authenticated.runAs(), is(notNullValue())); @@ -689,74 +653,60 @@ public class InternalAuthenticationServiceTests extends ESTestCase { assertThat(authenticated.roles(), arrayContaining("user")); assertThat(authenticated.runAs().principal(), is("looked up user")); assertThat(authenticated.runAs().roles(), arrayContaining("some role")); - User user1 = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - assertThat(user1, sameInstance(authenticated)); - } - - public void testRunAsLookupDifferentRealmRest() throws Exception { - AuthenticationToken token = mock(AuthenticationToken.class); - threadContext.putHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, "run_as"); - when(secondRealm.token(threadContext)).thenReturn(token); - when(secondRealm.supports(token)).thenReturn(true); - when(secondRealm.authenticate(token)).thenReturn(new User("lookup user", new String[]{"user"})); - when(firstRealm.lookupUser("run_as")).thenReturn(new User("looked up user", new String[]{"some role"})); - when(firstRealm.userLookupSupported()).thenReturn(true); - - User authenticated = service.authenticate(restRequest); - - assertThat(SystemUser.is(authenticated), is(false)); - assertThat(authenticated.runAs(), is(notNullValue())); - assertThat(authenticated.principal(), is("lookup user")); - assertThat(authenticated.roles(), arrayContaining("user")); - assertThat(authenticated.runAs().principal(), is("looked up user")); - assertThat(authenticated.runAs().roles(), arrayContaining("some role")); - User user1 = threadContext.getTransient(InternalAuthenticationService.USER_KEY); - assertThat(user1, sameInstance(authenticated)); + assertThreadContextContainsAuthentication(result); } public void testRunAsWithEmptyRunAsUsernameRest() throws Exception { AuthenticationToken token = mock(AuthenticationToken.class); + User user = new User("lookup user", new String[]{"user"}); threadContext.putHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, ""); when(secondRealm.token(threadContext)).thenReturn(token); when(secondRealm.supports(token)).thenReturn(true); - when(secondRealm.authenticate(token)).thenReturn(new User("lookup user", new String[]{"user"})); + when(secondRealm.authenticate(token)).thenReturn(user); when(secondRealm.userLookupSupported()).thenReturn(true); try { service.authenticate(restRequest); fail("exception should be thrown"); } catch (ElasticsearchException e) { - verify(auditTrail).authenticationFailed(token, restRequest); + verify(auditTrail).runAsDenied(any(User.class), eq(restRequest)); verifyNoMoreInteractions(auditTrail); } } public void testRunAsWithEmptyRunAsUsername() throws Exception { AuthenticationToken token = mock(AuthenticationToken.class); + User user = new User("lookup user", new String[]{"user"}); threadContext.putHeader(InternalAuthenticationService.RUN_AS_USER_HEADER, ""); when(secondRealm.token(threadContext)).thenReturn(token); when(secondRealm.supports(token)).thenReturn(true); - when(secondRealm.authenticate(token)).thenReturn(new User("lookup user", new String[]{"user"})); + when(secondRealm.authenticate(token)).thenReturn(user); when(secondRealm.userLookupSupported()).thenReturn(true); try { service.authenticate("_action", message, null); fail("exception should be thrown"); } catch (ElasticsearchException e) { - verify(auditTrail).authenticationFailed(token, "_action", message); + verify(auditTrail).runAsDenied(any(User.class), eq("_action"), eq(message)); verifyNoMoreInteractions(auditTrail); } } - public void testVersionWrittenWithUser() throws Exception { - User user = new User("username", "r1", "r2", "r3"); - String text = InternalAuthenticationService.encodeUser(user, null); - - StreamInput input = StreamInput.wrap(Base64.getDecoder().decode(text)); - Version version = Version.readVersion(input); - assertThat(version, is(Version.CURRENT)); - } - private static class InternalMessage extends TransportMessage { } + + void assertThreadContextContainsAuthentication(Authentication authentication) throws IOException { + assertThreadContextContainsAuthentication(authentication, true); + } + + void assertThreadContextContainsAuthentication(Authentication authentication, boolean sign) throws IOException { + Authentication contextAuth = threadContext.getTransient(Authentication.AUTHENTICATION_KEY); + assertThat(contextAuth, notNullValue()); + assertThat(contextAuth, is(authentication)); + if (sign) { + assertThat(threadContext.getHeader(Authentication.AUTHENTICATION_KEY), equalTo((Object) "_signed_auth")); + } else { + assertThat(threadContext.getHeader(Authentication.AUTHENTICATION_KEY), equalTo((Object) authentication.encode())); + } + } } diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/InternalAuthorizationServiceTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/InternalAuthorizationServiceTests.java index 69b07d08634..b242a89f710 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/InternalAuthorizationServiceTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/InternalAuthorizationServiceTests.java @@ -56,6 +56,8 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; import org.elasticsearch.search.action.SearchTransportService; import org.elasticsearch.shield.ShieldTemplateService; +import org.elasticsearch.shield.authc.Authentication; +import org.elasticsearch.shield.authc.Authentication.RealmRef; import org.elasticsearch.shield.user.AnonymousUser; import org.elasticsearch.shield.user.SystemUser; import org.elasticsearch.shield.user.User; @@ -123,10 +125,10 @@ public class InternalAuthorizationServiceTests extends ESTestCase { TransportRequest request = mock(TransportRequest.class); // A failure would throw an exception - internalAuthorizationService.authorize(SystemUser.INSTANCE, "indices:monitor/whatever", request); + internalAuthorizationService.authorize(createAuthentication(SystemUser.INSTANCE), "indices:monitor/whatever", request); verify(auditTrail).accessGranted(SystemUser.INSTANCE, "indices:monitor/whatever", request); - internalAuthorizationService.authorize(SystemUser.INSTANCE, "internal:whatever", request); + internalAuthorizationService.authorize(createAuthentication(SystemUser.INSTANCE), "internal:whatever", request); verify(auditTrail).accessGranted(SystemUser.INSTANCE, "internal:whatever", request); verifyNoMoreInteractions(auditTrail); } @@ -134,7 +136,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { public void testIndicesActionsAreNotAuthorized() { TransportRequest request = mock(TransportRequest.class); try { - internalAuthorizationService.authorize(SystemUser.INSTANCE, "indices:", request); + internalAuthorizationService.authorize(createAuthentication(SystemUser.INSTANCE), "indices:", request); fail("action beginning with indices should have failed"); } catch (ElasticsearchSecurityException e) { assertAuthorizationException(e, @@ -147,7 +149,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { public void testClusterAdminActionsAreNotAuthorized() { TransportRequest request = mock(TransportRequest.class); try { - internalAuthorizationService.authorize(SystemUser.INSTANCE, "cluster:admin/whatever", request); + internalAuthorizationService.authorize(createAuthentication(SystemUser.INSTANCE), "cluster:admin/whatever", request); fail("action beginning with cluster:admin/whatever should have failed"); } catch (ElasticsearchSecurityException e) { assertAuthorizationException(e, @@ -160,7 +162,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { public void testClusterAdminSnapshotStatusActionIsNotAuthorized() { TransportRequest request = mock(TransportRequest.class); try { - internalAuthorizationService.authorize(SystemUser.INSTANCE, "cluster:admin/snapshot/status", request); + internalAuthorizationService.authorize(createAuthentication(SystemUser.INSTANCE), "cluster:admin/snapshot/status", request); fail("action beginning with cluster:admin/snapshot/status should have failed"); } catch (ElasticsearchSecurityException e) { assertAuthorizationException(e, containsString("action [cluster:admin/snapshot/status] is unauthorized for user [" + @@ -174,7 +176,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { TransportRequest request = new SearchRequest(); User user = new User("test user"); try { - internalAuthorizationService.authorize(user, "indices:a", request); + internalAuthorizationService.authorize(createAuthentication(user), "indices:a", request); fail("user without roles should be denied"); } catch (ElasticsearchSecurityException e) { assertAuthorizationException(e, containsString("action [indices:a] is unauthorized for user [test user]")); @@ -187,7 +189,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { TransportRequest request = new SearchRequest(); User user = new User("test user", "non-existent-role"); try { - internalAuthorizationService.authorize(user, "indices:a", request); + internalAuthorizationService.authorize(createAuthentication(user), "indices:a", request); fail("user with unknown role only should have been denied"); } catch (ElasticsearchSecurityException e) { assertAuthorizationException(e, containsString("action [indices:a] is unauthorized for user [test user]")); @@ -202,7 +204,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { when(rolesStore.role("a_all")).thenReturn(Role.builder("a_role").add(IndexPrivilege.ALL, "a").build()); try { - internalAuthorizationService.authorize(user, "whatever", request); + internalAuthorizationService.authorize(createAuthentication(user), "whatever", request); fail("non indices and non cluster requests should be denied"); } catch (ElasticsearchSecurityException e) { assertAuthorizationException(e, containsString("action [whatever] is unauthorized for user [test user]")); @@ -217,7 +219,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { when(rolesStore.role("no_indices")).thenReturn(Role.builder("no_indices").cluster(ClusterPrivilege.action("")).build()); try { - internalAuthorizationService.authorize(user, "indices:a", request); + internalAuthorizationService.authorize(createAuthentication(user), "indices:a", request); fail("user only has cluster roles so indices requests should fail"); } catch (ElasticsearchSecurityException e) { assertAuthorizationException(e, containsString("action [indices:a] is unauthorized for user [test user]")); @@ -231,28 +233,29 @@ public class InternalAuthorizationServiceTests extends ESTestCase { when(rolesStore.role("a_all")).thenReturn(Role.builder("a_role").add(IndexPrivilege.ALL, "a").build()); ClearScrollRequest clearScrollRequest = new ClearScrollRequest(); - internalAuthorizationService.authorize(user, ClearScrollAction.NAME, clearScrollRequest); + internalAuthorizationService.authorize(createAuthentication(user), ClearScrollAction.NAME, clearScrollRequest); verify(auditTrail).accessGranted(user, ClearScrollAction.NAME, clearScrollRequest); SearchScrollRequest searchScrollRequest = new SearchScrollRequest(); - internalAuthorizationService.authorize(user, SearchScrollAction.NAME, searchScrollRequest); + internalAuthorizationService.authorize(createAuthentication(user), SearchScrollAction.NAME, searchScrollRequest); verify(auditTrail).accessGranted(user, SearchScrollAction.NAME, searchScrollRequest); // We have to use a mock request for other Scroll actions as the actual requests are package private to SearchTransportService TransportRequest request = mock(TransportRequest.class); - internalAuthorizationService.authorize(user, SearchTransportService.CLEAR_SCROLL_CONTEXTS_ACTION_NAME, request); + internalAuthorizationService + .authorize(createAuthentication(user), SearchTransportService.CLEAR_SCROLL_CONTEXTS_ACTION_NAME, request); verify(auditTrail).accessGranted(user, SearchTransportService.CLEAR_SCROLL_CONTEXTS_ACTION_NAME, request); - internalAuthorizationService.authorize(user, SearchTransportService.FETCH_ID_SCROLL_ACTION_NAME, request); + internalAuthorizationService.authorize(createAuthentication(user), SearchTransportService.FETCH_ID_SCROLL_ACTION_NAME, request); verify(auditTrail).accessGranted(user, SearchTransportService.FETCH_ID_SCROLL_ACTION_NAME, request); - internalAuthorizationService.authorize(user, SearchTransportService.QUERY_FETCH_SCROLL_ACTION_NAME, request); + internalAuthorizationService.authorize(createAuthentication(user), SearchTransportService.QUERY_FETCH_SCROLL_ACTION_NAME, request); verify(auditTrail).accessGranted(user, SearchTransportService.QUERY_FETCH_SCROLL_ACTION_NAME, request); - internalAuthorizationService.authorize(user, SearchTransportService.QUERY_SCROLL_ACTION_NAME, request); + internalAuthorizationService.authorize(createAuthentication(user), SearchTransportService.QUERY_SCROLL_ACTION_NAME, request); verify(auditTrail).accessGranted(user, SearchTransportService.QUERY_SCROLL_ACTION_NAME, request); - internalAuthorizationService.authorize(user, SearchTransportService.FREE_CONTEXT_SCROLL_ACTION_NAME, request); + internalAuthorizationService.authorize(createAuthentication(user), SearchTransportService.FREE_CONTEXT_SCROLL_ACTION_NAME, request); verify(auditTrail).accessGranted(user, SearchTransportService.FREE_CONTEXT_SCROLL_ACTION_NAME, request); verifyNoMoreInteractions(auditTrail); } @@ -266,7 +269,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA); try { - internalAuthorizationService.authorize(user, "indices:a", request); + internalAuthorizationService.authorize(createAuthentication(user), "indices:a", request); fail("indices request for b should be denied since there is no such index"); } catch (ElasticsearchSecurityException e) { assertAuthorizationException(e, containsString("action [indices:a] is unauthorized for user [test user]")); @@ -287,7 +290,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA); try { - internalAuthorizationService.authorize(user, CreateIndexAction.NAME, request); + internalAuthorizationService.authorize(createAuthentication(user), CreateIndexAction.NAME, request); fail("indices creation request with alias should be denied since user does not have permission to alias"); } catch (ElasticsearchSecurityException e) { assertAuthorizationException(e, @@ -308,7 +311,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { when(clusterService.state()).thenReturn(state); when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA); - internalAuthorizationService.authorize(user, CreateIndexAction.NAME, request); + internalAuthorizationService.authorize(createAuthentication(user), CreateIndexAction.NAME, request); verify(auditTrail).accessGranted(user, CreateIndexAction.NAME, request); verifyNoMoreInteractions(auditTrail); @@ -364,7 +367,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA); try { - internalAuthorizationService.authorize(AnonymousUser.INSTANCE, "indices:a", request); + internalAuthorizationService.authorize(createAuthentication(AnonymousUser.INSTANCE), "indices:a", request); fail("indices request for b should be denied since there is no such index"); } catch (ElasticsearchSecurityException e) { assertAuthorizationException(e, @@ -395,7 +398,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA); try { - internalAuthorizationService.authorize(anonymousUser, "indices:a", request); + internalAuthorizationService.authorize(createAuthentication(anonymousUser), "indices:a", request); fail("indices request for b should be denied since there is no such index"); } catch (ElasticsearchSecurityException e) { assertAuthenticationException(e, containsString("action [indices:a] requires authentication")); @@ -411,7 +414,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { User user = new User("test user", null, new User("run as me", new String[] { "admin" })); assertThat(user.runAs(), is(notNullValue())); try { - internalAuthorizationService.authorize(user, "indices:a", request); + internalAuthorizationService.authorize(createAuthentication(user), "indices:a", request); fail("user without roles should be denied for run as"); } catch (ElasticsearchSecurityException e) { assertAuthorizationException(e, containsString("action [indices:a] is unauthorized for user [test user] run as [run as me]")); @@ -431,7 +434,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { .build()); try { - internalAuthorizationService.authorize(user, "indices:a", request); + internalAuthorizationService.authorize(createAuthentication(user), "indices:a", request); fail("user without roles should be denied for run as"); } catch (ElasticsearchSecurityException e) { assertAuthorizationException(e, containsString("action [indices:a] is unauthorized for user [test user] run as [run as me]")); @@ -465,7 +468,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { } try { - internalAuthorizationService.authorize(user, "indices:a", request); + internalAuthorizationService.authorize(createAuthentication(user), "indices:a", request); fail("the run as user's role doesn't exist so they should not get authorized"); } catch (ElasticsearchSecurityException e) { assertAuthorizationException(e, containsString("action [indices:a] is unauthorized for user [test user] run as [run as me]")); @@ -496,7 +499,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { .add(IndexPrivilege.ALL, "b") .build()); - internalAuthorizationService.authorize(user, "indices:a", request); + internalAuthorizationService.authorize(createAuthentication(user), "indices:a", request); verify(auditTrail).runAsGranted(user, "indices:a", request); verify(auditTrail).accessGranted(user, "indices:a", request); verifyNoMoreInteractions(auditTrail); @@ -533,7 +536,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { String action = requestTuple.v1(); TransportRequest request = requestTuple.v2(); try { - internalAuthorizationService.authorize(user, action, request); + internalAuthorizationService.authorize(createAuthentication(user), action, request); fail("only the xpack user can execute operation [" + action + "] against the internal index"); } catch (ElasticsearchSecurityException e) { assertAuthorizationException(e, containsString("action [" + action + "] is unauthorized for user [all_access_user]")); @@ -544,12 +547,12 @@ public class InternalAuthorizationServiceTests extends ESTestCase { // we should allow waiting for the health of the index or any index if the user has this permission ClusterHealthRequest request = new ClusterHealthRequest(ShieldTemplateService.SECURITY_INDEX_NAME); - internalAuthorizationService.authorize(user, ClusterHealthAction.NAME, request); + internalAuthorizationService.authorize(createAuthentication(user), ClusterHealthAction.NAME, request); verify(auditTrail).accessGranted(user, ClusterHealthAction.NAME, request); // multiple indices request = new ClusterHealthRequest(ShieldTemplateService.SECURITY_INDEX_NAME, "foo", "bar"); - internalAuthorizationService.authorize(user, ClusterHealthAction.NAME, request); + internalAuthorizationService.authorize(createAuthentication(user), ClusterHealthAction.NAME, request); verify(auditTrail).accessGranted(user, ClusterHealthAction.NAME, request); } @@ -580,7 +583,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { for (Tuple requestTuple : requests) { String action = requestTuple.v1(); TransportRequest request = requestTuple.v2(); - internalAuthorizationService.authorize(user, action, request); + internalAuthorizationService.authorize(createAuthentication(user), action, request); verify(auditTrail).accessGranted(user, action, request); } } @@ -612,8 +615,13 @@ public class InternalAuthorizationServiceTests extends ESTestCase { for (Tuple requestTuple : requests) { String action = requestTuple.v1(); TransportRequest request = requestTuple.v2(); - internalAuthorizationService.authorize(XPackUser.INSTANCE, action, request); + internalAuthorizationService.authorize(createAuthentication(XPackUser.INSTANCE), action, request); verify(auditTrail).accessGranted(XPackUser.INSTANCE, action, request); } } + + private Authentication createAuthentication(User user) { + RealmRef lookedUpBy = user.runAs() == null ? null : new RealmRef("looked", "up", "by"); + return new Authentication(user, new RealmRef("test", "test", "foo"), lookedUpBy); + } } diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/DefaultRoleTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/DefaultRoleTests.java index 5e7f52ed623..fad804c08aa 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/DefaultRoleTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/DefaultRoleTests.java @@ -12,6 +12,14 @@ import org.elasticsearch.client.Client; import org.elasticsearch.license.plugin.action.get.GetLicenseAction; import org.elasticsearch.shield.action.user.AuthenticateRequestBuilder; import org.elasticsearch.shield.action.user.ChangePasswordRequestBuilder; +import org.elasticsearch.shield.authc.Authentication; +import org.elasticsearch.shield.authc.Authentication.RealmRef; +import org.elasticsearch.shield.authc.activedirectory.ActiveDirectoryRealm; +import org.elasticsearch.shield.authc.esnative.NativeRealm; +import org.elasticsearch.shield.authc.esnative.ReservedRealm; +import org.elasticsearch.shield.authc.file.FileRealm; +import org.elasticsearch.shield.authc.ldap.LdapRealm; +import org.elasticsearch.shield.authc.pki.PkiRealm; import org.elasticsearch.shield.user.User; import org.elasticsearch.shield.action.user.AuthenticateAction; import org.elasticsearch.shield.action.user.AuthenticateRequest; @@ -28,7 +36,11 @@ import java.util.Iterator; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; +import static org.mockito.Mockito.when; /** * Unit tests for the {@link DefaultRole} @@ -51,9 +63,16 @@ public class DefaultRoleTests extends ESTestCase { new ChangePasswordRequestBuilder(mock(Client.class)).username(user.principal()).request() : new AuthenticateRequestBuilder(mock(Client.class)).username(user.principal()).request(); final String action = changePasswordRequest ? ChangePasswordAction.NAME : AuthenticateAction.NAME; - assertThat(request, instanceOf(UserRequest.class)); + final Authentication authentication = mock(Authentication.class); + final RealmRef authenticatedBy = mock(RealmRef.class); + when(authentication.getUser()).thenReturn(user); + when(authentication.getRunAsUser()).thenReturn(user); + when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy); + when(authenticatedBy.getType()) + .thenReturn(changePasswordRequest ? randomFrom(ReservedRealm.TYPE, NativeRealm.TYPE) : randomAsciiOfLengthBetween(4, 12)); - assertThat(DefaultRole.INSTANCE.cluster().check(action, request, user), is(true)); + assertThat(request, instanceOf(UserRequest.class)); + assertThat(DefaultRole.INSTANCE.cluster().check(action, request, authentication), is(true)); } public void testDefaultRoleDoesNotAllowNonMatchingUsername() { @@ -64,19 +83,33 @@ public class DefaultRoleTests extends ESTestCase { new ChangePasswordRequestBuilder(mock(Client.class)).username(username).request() : new AuthenticateRequestBuilder(mock(Client.class)).username(username).request(); final String action = changePasswordRequest ? ChangePasswordAction.NAME : AuthenticateAction.NAME; - assertThat(request, instanceOf(UserRequest.class)); + final Authentication authentication = mock(Authentication.class); + final RealmRef authenticatedBy = mock(RealmRef.class); + when(authentication.getUser()).thenReturn(user); + when(authentication.getRunAsUser()).thenReturn(user); + when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy); + when(authenticatedBy.getType()) + .thenReturn(changePasswordRequest ? randomFrom(ReservedRealm.TYPE, NativeRealm.TYPE) : randomAsciiOfLengthBetween(4, 12)); - assertThat(DefaultRole.INSTANCE.cluster().check(action, request, user), is(false)); + assertThat(request, instanceOf(UserRequest.class)); + assertThat(DefaultRole.INSTANCE.cluster().check(action, request, authentication), is(false)); final User user2 = new User("admin", new String[] { "bar" }, user); + when(authentication.getUser()).thenReturn(user2); + when(authentication.getRunAsUser()).thenReturn(user); + final RealmRef lookedUpBy = mock(RealmRef.class); + when(authentication.getLookedUpBy()).thenReturn(lookedUpBy); + when(lookedUpBy.getType()) + .thenReturn(changePasswordRequest ? randomFrom(ReservedRealm.TYPE, NativeRealm.TYPE) : randomAsciiOfLengthBetween(4, 12)); + // this should still fail since the username is still different + assertThat(DefaultRole.INSTANCE.cluster().check(action, request, authentication), is(false)); + if (request instanceof ChangePasswordRequest) { ((ChangePasswordRequest)request).username("joe"); } else { ((AuthenticateRequest)request).username("joe"); } - // run as should not be checked by this role, it is up to the caller to provide the correct user - assertThat(DefaultRole.INSTANCE.cluster().check(action, request, user2), is(false)); - assertThat(DefaultRole.INSTANCE.cluster().check(action, request, user), is(true)); + assertThat(DefaultRole.INSTANCE.cluster().check(action, request, authentication), is(true)); } public void testDefaultRoleDoesNotAllowOtherActions() { @@ -84,8 +117,85 @@ public class DefaultRoleTests extends ESTestCase { final TransportRequest request = mock(TransportRequest.class); final String action = randomFrom(PutUserAction.NAME, DeleteUserAction.NAME, ClusterHealthAction.NAME, ClusterStateAction.NAME, ClusterStatsAction.NAME, GetLicenseAction.NAME); + final Authentication authentication = mock(Authentication.class); + final RealmRef authenticatedBy = mock(RealmRef.class); + when(authentication.getUser()).thenReturn(user); + when(authentication.getRunAsUser()).thenReturn(randomBoolean() ? user : new User("runAs")); + when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy); + when(authenticatedBy.getType()) + .thenReturn(randomAsciiOfLengthBetween(4, 12)); - assertThat(DefaultRole.INSTANCE.cluster().check(action, request, user), is(false)); - verifyZeroInteractions(user, request); + assertThat(DefaultRole.INSTANCE.cluster().check(action, request, authentication), is(false)); + verifyZeroInteractions(user, request, authentication); + } + + public void testDefaultRoleWithRunAsChecksAuthenticatedBy() { + final String username = "joe"; + final User runAs = new User(username); + final User user = new User("admin", new String[] { "bar" }, runAs); + final boolean changePasswordRequest = randomBoolean(); + final TransportRequest request = changePasswordRequest ? + new ChangePasswordRequestBuilder(mock(Client.class)).username(username).request() : + new AuthenticateRequestBuilder(mock(Client.class)).username(username).request(); + final String action = changePasswordRequest ? ChangePasswordAction.NAME : AuthenticateAction.NAME; + final Authentication authentication = mock(Authentication.class); + final RealmRef authenticatedBy = mock(RealmRef.class); + final RealmRef lookedUpBy = mock(RealmRef.class); + when(authentication.getUser()).thenReturn(user); + when(authentication.getRunAsUser()).thenReturn(runAs); + when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy); + when(authentication.getLookedUpBy()).thenReturn(lookedUpBy); + when(lookedUpBy.getType()) + .thenReturn(changePasswordRequest ? randomFrom(ReservedRealm.TYPE, NativeRealm.TYPE) : randomAsciiOfLengthBetween(4, 12)); + + assertThat(DefaultRole.INSTANCE.cluster().check(action, request, authentication), is(true)); + + when(authentication.getRunAsUser()).thenReturn(user); + assertThat(DefaultRole.INSTANCE.cluster().check(action, request, authentication), is(false)); + } + + public void testDefaultRoleDoesNotAllowChangePasswordForOtherRealms() { + final User user = new User("joe"); + final ChangePasswordRequest request = new ChangePasswordRequestBuilder(mock(Client.class)).username(user.principal()).request(); + final String action = ChangePasswordAction.NAME; + final Authentication authentication = mock(Authentication.class); + final RealmRef authenticatedBy = mock(RealmRef.class); + when(authentication.getUser()).thenReturn(user); + when(authentication.getRunAsUser()).thenReturn(user); + when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy); + when(authenticatedBy.getType()).thenReturn(randomFrom(LdapRealm.TYPE, FileRealm.TYPE, ActiveDirectoryRealm.TYPE, PkiRealm.TYPE, + randomAsciiOfLengthBetween(4, 12))); + + assertThat(request, instanceOf(UserRequest.class)); + assertThat(DefaultRole.INSTANCE.cluster().check(action, request, authentication), is(false)); + verify(authenticatedBy).getType(); + verify(authentication, times(2)).getRunAsUser(); + verify(authentication).getUser(); + verify(authentication).getAuthenticatedBy(); + verifyNoMoreInteractions(authenticatedBy, authentication); + } + + public void testDefaultRoleDoesNotAllowChangePasswordForLookedUpByOtherRealms() { + final User runAs = new User("joe"); + final User user = new User("admin", new String[] { "bar" }, runAs); + final ChangePasswordRequest request = new ChangePasswordRequestBuilder(mock(Client.class)).username(runAs.principal()).request(); + final String action = ChangePasswordAction.NAME; + final Authentication authentication = mock(Authentication.class); + final RealmRef authenticatedBy = mock(RealmRef.class); + final RealmRef lookedUpBy = mock(RealmRef.class); + when(authentication.getUser()).thenReturn(user); + when(authentication.getRunAsUser()).thenReturn(runAs); + when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy); + when(authentication.getLookedUpBy()).thenReturn(lookedUpBy); + when(lookedUpBy.getType()).thenReturn(randomFrom(LdapRealm.TYPE, FileRealm.TYPE, ActiveDirectoryRealm.TYPE, PkiRealm.TYPE, + randomAsciiOfLengthBetween(4, 12))); + + assertThat(request, instanceOf(UserRequest.class)); + assertThat(DefaultRole.INSTANCE.cluster().check(action, request, authentication), is(false)); + verify(authentication).getLookedUpBy(); + verify(authentication, times(2)).getRunAsUser(); + verify(authentication).getUser(); + verify(lookedUpBy).getType(); + verifyNoMoreInteractions(authentication, lookedUpBy, authenticatedBy); } } diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/KibanaRoleTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/KibanaRoleTests.java index 6aefb0b1f5e..f199c3008b7 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/KibanaRoleTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/KibanaRoleTests.java @@ -17,14 +17,14 @@ import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateActio import org.elasticsearch.action.delete.DeleteAction; import org.elasticsearch.action.index.IndexAction; import org.elasticsearch.marvel.action.MonitoringBulkAction; -import org.elasticsearch.shield.user.KibanaUser; -import org.elasticsearch.shield.user.User; +import org.elasticsearch.shield.authc.Authentication; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.transport.TransportRequest; import java.util.Arrays; import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; /** * Tests for the kibana role @@ -32,15 +32,15 @@ import static org.hamcrest.Matchers.is; public class KibanaRoleTests extends ESTestCase { public void testCluster() { - final User user = KibanaUser.INSTANCE; final TransportRequest request = new TransportRequest.Empty(); - assertThat(KibanaRole.INSTANCE.cluster().check(ClusterHealthAction.NAME, request, user), is(true)); - assertThat(KibanaRole.INSTANCE.cluster().check(ClusterStateAction.NAME, request, user), is(true)); - assertThat(KibanaRole.INSTANCE.cluster().check(ClusterStatsAction.NAME, request, user), is(true)); - assertThat(KibanaRole.INSTANCE.cluster().check(MonitoringBulkAction.NAME, request, user), is(true)); - assertThat(KibanaRole.INSTANCE.cluster().check(PutIndexTemplateAction.NAME, request, user), is(false)); - assertThat(KibanaRole.INSTANCE.cluster().check(ClusterRerouteAction.NAME, request, user), is(false)); - assertThat(KibanaRole.INSTANCE.cluster().check(ClusterUpdateSettingsAction.NAME, request, user), is(false)); + final Authentication authentication = mock(Authentication.class); + assertThat(KibanaRole.INSTANCE.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); + assertThat(KibanaRole.INSTANCE.cluster().check(ClusterStateAction.NAME, request, authentication), is(true)); + assertThat(KibanaRole.INSTANCE.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true)); + assertThat(KibanaRole.INSTANCE.cluster().check(PutIndexTemplateAction.NAME, request, authentication), is(false)); + assertThat(KibanaRole.INSTANCE.cluster().check(ClusterRerouteAction.NAME, request, authentication), is(false)); + assertThat(KibanaRole.INSTANCE.cluster().check(ClusterUpdateSettingsAction.NAME, request, authentication), is(false)); + assertThat(KibanaRole.INSTANCE.cluster().check(MonitoringBulkAction.NAME, request, authentication), is(true)); } public void testRunAs() { diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/KibanaUserRoleTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/KibanaUserRoleTests.java index ccdf3b4fdc8..89021551d05 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/KibanaUserRoleTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/KibanaUserRoleTests.java @@ -19,27 +19,27 @@ import org.elasticsearch.action.index.IndexAction; import org.elasticsearch.action.search.MultiSearchAction; import org.elasticsearch.action.search.SearchAction; import org.elasticsearch.marvel.action.MonitoringBulkAction; -import org.elasticsearch.shield.user.User; +import org.elasticsearch.shield.authc.Authentication; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.transport.TransportRequest; import java.util.Arrays; import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; public class KibanaUserRoleTests extends ESTestCase { public void testCluster() { - final User user = new User("joe"); + final Authentication authentication = mock(Authentication.class); final TransportRequest request = new TransportRequest.Empty(); - assertThat(KibanaUserRole.INSTANCE.cluster().check(ClusterHealthAction.NAME, request, user), is(true)); - assertThat(KibanaUserRole.INSTANCE.cluster().check(ClusterStateAction.NAME, request, user), is(true)); - assertThat(KibanaUserRole.INSTANCE.cluster().check(ClusterStatsAction.NAME, request, user), is(true)); - assertThat(KibanaUserRole.INSTANCE.cluster().check(PutIndexTemplateAction.NAME, request, user), is(false)); - assertThat(KibanaUserRole.INSTANCE.cluster().check(ClusterRerouteAction.NAME, request, user), is(false)); - assertThat(KibanaUserRole.INSTANCE.cluster().check(ClusterUpdateSettingsAction.NAME, request, user), is(false)); - - assertThat(KibanaUserRole.INSTANCE.cluster().check(MonitoringBulkAction.NAME, request, user), is(false)); + assertThat(KibanaUserRole.INSTANCE.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); + assertThat(KibanaUserRole.INSTANCE.cluster().check(ClusterStateAction.NAME, request, authentication), is(true)); + assertThat(KibanaUserRole.INSTANCE.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true)); + assertThat(KibanaUserRole.INSTANCE.cluster().check(PutIndexTemplateAction.NAME, request, authentication), is(false)); + assertThat(KibanaUserRole.INSTANCE.cluster().check(ClusterRerouteAction.NAME, request, authentication), is(false)); + assertThat(KibanaUserRole.INSTANCE.cluster().check(ClusterUpdateSettingsAction.NAME, request, authentication), is(false)); + assertThat(KibanaUserRole.INSTANCE.cluster().check(MonitoringBulkAction.NAME, request, authentication), is(false)); } public void testRunAs() { diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/SuperuserRoleTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/SuperuserRoleTests.java index 5b94d3eaf29..cd47c9e1bee 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/SuperuserRoleTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/SuperuserRoleTests.java @@ -20,6 +20,7 @@ import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.set.Sets; import org.elasticsearch.shield.action.role.PutRoleAction; import org.elasticsearch.shield.action.user.PutUserAction; +import org.elasticsearch.shield.authc.Authentication; import org.elasticsearch.shield.authz.accesscontrol.IndicesAccessControl.IndexAccessControl; import org.elasticsearch.shield.user.User; import org.elasticsearch.test.ESTestCase; @@ -28,6 +29,8 @@ import org.elasticsearch.transport.TransportRequest; import java.util.Map; import static org.hamcrest.Matchers.is; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; /** * Tests for the superuser role @@ -36,14 +39,16 @@ public class SuperuserRoleTests extends ESTestCase { public void testCluster() { final User user = new User("joe", SuperuserRole.NAME); + final Authentication authentication = mock(Authentication.class); + when(authentication.getUser()).thenReturn(user); final TransportRequest request = new TransportRequest.Empty(); - assertThat(SuperuserRole.INSTANCE.cluster().check(ClusterHealthAction.NAME, request, user), is(true)); - assertThat(SuperuserRole.INSTANCE.cluster().check(ClusterUpdateSettingsAction.NAME, request, user), is(true)); - assertThat(SuperuserRole.INSTANCE.cluster().check(PutUserAction.NAME, request, user), is(true)); - assertThat(SuperuserRole.INSTANCE.cluster().check(PutRoleAction.NAME, request, user), is(true)); - assertThat(SuperuserRole.INSTANCE.cluster().check(PutIndexTemplateAction.NAME, request, user), is(true)); - assertThat(SuperuserRole.INSTANCE.cluster().check("internal:admin/foo", request, user), is(false)); + assertThat(SuperuserRole.INSTANCE.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true)); + assertThat(SuperuserRole.INSTANCE.cluster().check(ClusterUpdateSettingsAction.NAME, request, authentication), is(true)); + assertThat(SuperuserRole.INSTANCE.cluster().check(PutUserAction.NAME, request, authentication), is(true)); + assertThat(SuperuserRole.INSTANCE.cluster().check(PutRoleAction.NAME, request, authentication), is(true)); + assertThat(SuperuserRole.INSTANCE.cluster().check(PutIndexTemplateAction.NAME, request, authentication), is(true)); + assertThat(SuperuserRole.INSTANCE.cluster().check("internal:admin/foo", request, authentication), is(false)); } public void testIndices() { diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/rest/ShieldRestFilterTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/rest/ShieldRestFilterTests.java index 59ae9601bf7..fac9f00da77 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/rest/ShieldRestFilterTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/rest/ShieldRestFilterTests.java @@ -12,7 +12,7 @@ import org.elasticsearch.rest.RestChannel; import org.elasticsearch.rest.RestController; import org.elasticsearch.rest.RestFilterChain; import org.elasticsearch.rest.RestRequest; -import org.elasticsearch.shield.user.User; +import org.elasticsearch.shield.authc.Authentication; import org.elasticsearch.shield.authc.AuthenticationService; import org.elasticsearch.shield.SecurityLicenseState; import org.elasticsearch.test.ESTestCase; @@ -52,8 +52,8 @@ public class ShieldRestFilterTests extends ESTestCase { public void testProcess() throws Exception { RestRequest request = mock(RestRequest.class); - User user = new User("_user", "r1"); - when(authcService.authenticate(request)).thenReturn(user); + Authentication authentication = mock(Authentication.class); + when(authcService.authenticate(request)).thenReturn(authentication); filter.process(request, channel, chain); verify(chain).continueProcessing(request, channel); verifyZeroInteractions(channel); diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/transport/ClientTransportFilterTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/transport/ClientTransportFilterTests.java index 19eb7ed089e..89e8fe15e5c 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/transport/ClientTransportFilterTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/transport/ClientTransportFilterTests.java @@ -30,6 +30,6 @@ public class ClientTransportFilterTests extends ESTestCase { public void testOutbound() throws Exception { TransportRequest request = mock(TransportRequest.class); filter.outbound("_action", request); - verify(authcService).attachUserHeaderIfMissing(SystemUser.INSTANCE); + verify(authcService).attachUserIfMissing(SystemUser.INSTANCE); } } diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/transport/ServerTransportFilterTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/transport/ServerTransportFilterTests.java index 715df3c07e0..9cd88467b32 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/transport/ServerTransportFilterTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/transport/ServerTransportFilterTests.java @@ -8,7 +8,7 @@ package org.elasticsearch.shield.transport; import org.elasticsearch.ElasticsearchSecurityException; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.concurrent.ThreadContext; -import org.elasticsearch.shield.user.User; +import org.elasticsearch.shield.authc.Authentication; import org.elasticsearch.shield.action.ShieldActionMapper; import org.elasticsearch.shield.authc.AuthenticationService; import org.elasticsearch.shield.authz.AuthorizationService; @@ -48,10 +48,10 @@ public class ServerTransportFilterTests extends ESTestCase { public void testInbound() throws Exception { TransportRequest request = mock(TransportRequest.class); - User user = mock(User.class); - when(authcService.authenticate("_action", request, null)).thenReturn(user); + Authentication authentication = mock(Authentication.class); + when(authcService.authenticate("_action", request, null)).thenReturn(authentication); filter.inbound("_action", request, channel); - verify(authzService).authorize(user, "_action", request); + verify(authzService).authorize(authentication, "_action", request); } public void testInboundAuthenticationException() throws Exception { @@ -68,9 +68,9 @@ public class ServerTransportFilterTests extends ESTestCase { public void testInboundAuthorizationException() throws Exception { TransportRequest request = mock(TransportRequest.class); - User user = mock(User.class); - when(authcService.authenticate("_action", request, null)).thenReturn(user); - doThrow(authorizationError("authz failed")).when(authzService).authorize(user, "_action", request); + Authentication authentication = mock(Authentication.class); + when(authcService.authenticate("_action", request, null)).thenReturn(authentication); + doThrow(authorizationError("authz failed")).when(authzService).authorize(authentication, "_action", request); try { filter.inbound("_action", request, channel); fail("expected filter inbound to throw an authorization exception on authorization error");