From eeb964c8869284cfe97543ae9ee5f99c86eb8f1c Mon Sep 17 00:00:00 2001 From: jaymode Date: Wed, 27 Apr 2016 11:49:13 -0400 Subject: [PATCH] security: default role checks authenticating realm This change makes the default role check the authenticating realm when authorizing a request for the current user (or run as user) where the user is trying to change their own password. We need to do this, otherwise we open up the potential of a user in one realm changing the password of a user in another realm. As part of this work, the authentication service has been refactored and simplified. A new object, Authentication, is now returned when authenticating. Currently, this object contains the user, authenticating realm information, and if it is a run as request the information of the realm that looked up the user. Closes elastic/elasticsearch#2089 Original commit: elastic/x-pack-elasticsearch@3fd9c37a166905e10249832abbfbd3eae4f168e3 --- .../shield/authz/AuthorizationUtilsTests.java | 12 +- .../elasticsearch/shield/InternalClient.java | 2 +- .../elasticsearch/shield/SecurityContext.java | 26 +- .../action/filter/ShieldActionFilter.java | 19 +- .../shield/audit/AuditTrail.java | 6 + .../shield/audit/AuditTrailService.java | 9 + .../shield/audit/index/IndexAuditTrail.java | 35 +- .../audit/logfile/LoggingAuditTrail.java | 11 + .../shield/authc/Authentication.java | 186 +++++ .../shield/authc/AuthenticationService.java | 12 +- .../authc/InternalAuthenticationService.java | 642 ++++++++---------- .../shield/authz/AuthorizationService.java | 9 +- .../shield/authz/AuthorizationUtils.java | 5 +- .../authz/InternalAuthorizationService.java | 98 +-- .../authz/permission/ClusterPermission.java | 12 +- .../shield/authz/permission/DefaultRole.java | 45 +- .../transport/ClientTransportFilter.java | 2 +- .../transport/ServerTransportFilter.java | 6 +- .../filter/ShieldActionFilterTests.java | 23 +- .../InternalAuthenticationServiceTests.java | 396 +++++------ .../InternalAuthorizationServiceTests.java | 68 +- .../authz/permission/DefaultRoleTests.java | 128 +++- .../authz/permission/KibanaRoleTests.java | 20 +- .../authz/permission/KibanaUserRoleTests.java | 20 +- .../authz/permission/SuperuserRoleTests.java | 17 +- .../shield/rest/ShieldRestFilterTests.java | 6 +- .../transport/ClientTransportFilterTests.java | 2 +- .../transport/ServerTransportFilterTests.java | 14 +- 28 files changed, 1074 insertions(+), 757 deletions(-) create mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authc/Authentication.java 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");