diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationEngine.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationEngine.java index 820da970a50bb..3faffddc904c2 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationEngine.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationEngine.java @@ -20,21 +20,17 @@ public interface AuthorizationEngine { - void resolveAuthorizationInfo(Authentication authentication, TransportRequest request, String action, - ActionListener listener); + void resolveAuthorizationInfo(RequestInfo requestInfo, ActionListener listener); - void authorizeRunAs(Authentication authentication, TransportRequest request, String action, AuthorizationInfo authorizationInfo, - ActionListener listener); + void authorizeRunAs(RequestInfo requestInfo, AuthorizationInfo authorizationInfo, ActionListener listener); - void authorizeClusterAction(Authentication authentication, TransportRequest request, String action, AuthorizationInfo authorizationInfo, - ActionListener listener); + void authorizeClusterAction(RequestInfo requestInfo, AuthorizationInfo authorizationInfo, ActionListener listener); - void authorizeIndexAction(Authentication authentication, TransportRequest request, String action, - AuthorizationInfo authorizationInfo, AsyncSupplier indicesAsyncSupplier, - Function aliasOrIndexFunction, + void authorizeIndexAction(RequestInfo requestInfo, AuthorizationInfo authorizationInfo, + AsyncSupplier indicesAsyncSupplier, Function aliasOrIndexFunction, ActionListener listener); - void loadAuthorizedIndices(Authentication authentication, String action, AuthorizationInfo info, + void loadAuthorizedIndices(RequestInfo requestInfo, AuthorizationInfo info, Map aliasAndIndexLookup, ActionListener> listener); interface AuthorizationInfo { @@ -58,6 +54,31 @@ public Map asMap() { } } + final class RequestInfo { + + private final Authentication authentication; + private final TransportRequest request; + private final String action; + + public RequestInfo(Authentication authentication, TransportRequest request, String action) { + this.authentication = authentication; + this.request = request; + this.action = action; + } + + public String getAction() { + return action; + } + + public Authentication getAuthentication() { + return authentication; + } + + public TransportRequest getRequest() { + return request; + } + } + class AuthorizationResult { private final boolean granted; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java index 08ecaeb7f9af4..914d917c6e472 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/AuthorizationService.java @@ -52,6 +52,7 @@ import org.elasticsearch.xpack.security.authz.AuthorizationEngine.AuthorizationResult; import org.elasticsearch.xpack.security.authz.AuthorizationEngine.EmptyAuthorizationInfo; import org.elasticsearch.xpack.security.authz.AuthorizationEngine.IndexAuthorizationResult; +import org.elasticsearch.xpack.security.authz.AuthorizationEngine.RequestInfo; import org.elasticsearch.xpack.security.authz.IndicesAndAliasesResolver.ResolvedIndices; import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore; @@ -145,78 +146,84 @@ public void authorize(final Authentication authentication, final String action, authorizeSystemUser(authentication, action, auditId, unwrappedRequest, listener); } else { final String finalAuditId = auditId; + final RequestInfo requestInfo = new RequestInfo(authentication, unwrappedRequest, action); final ActionListener authzInfoListener = wrapPreservingContext(ActionListener.wrap( authorizationInfo -> { putTransientIfNonExisting(AUTHORIZATION_INFO_KEY, authorizationInfo); - maybeAuthorizeRunAs(authentication, action, unwrappedRequest, finalAuditId, authorizationInfo, listener); + maybeAuthorizeRunAs(requestInfo, finalAuditId, authorizationInfo, listener); }, listener::onFailure), threadContext); - getAuthorizationEngine(authentication).resolveAuthorizationInfo(authentication, unwrappedRequest, action, authzInfoListener); + getAuthorizationEngine(authentication).resolveAuthorizationInfo(requestInfo, authzInfoListener); } } - private void maybeAuthorizeRunAs(final Authentication authentication, final String action, final TransportRequest unwrappedRequest, - final String requestId, final AuthorizationInfo authzInfo, final ActionListener listener) { + private void maybeAuthorizeRunAs(final RequestInfo requestInfo, final String requestId, final AuthorizationInfo authzInfo, + final ActionListener listener) { + final Authentication authentication = requestInfo.getAuthentication(); + final TransportRequest request = requestInfo.getRequest(); + final String action = requestInfo.getAction(); final boolean isRunAs = authentication.getUser().isRunAs(); if (isRunAs) { ActionListener runAsListener = wrapPreservingContext(ActionListener.wrap(result -> { if (result.isGranted()) { if (result.isAuditable()) { - auditTrail.runAsGranted(requestId, authentication, action, unwrappedRequest, + auditTrail.runAsGranted(requestId, authentication, action, request, authzInfo.getAuthenticatedUserAuthorizationInfo()); } - authorizeAction(authentication, action, requestId, unwrappedRequest, authzInfo, listener); + authorizeAction(requestInfo, requestId, authzInfo, listener); } else { - listener.onFailure(denyRunAs(requestId, authentication, action, unwrappedRequest, + listener.onFailure(denyRunAs(requestId, authentication, action, request, authzInfo.getAuthenticatedUserAuthorizationInfo())); } }, e -> { // TODO need a failure handler better than this! - listener.onFailure(denyRunAs(requestId, authentication, action, unwrappedRequest, authzInfo, e)); + listener.onFailure(denyRunAs(requestId, authentication, action, request, authzInfo, e)); }), threadContext); - authorizeRunAs(authentication, action, requestId, unwrappedRequest, authzInfo, runAsListener); + authorizeRunAs(requestInfo, requestId, authzInfo, runAsListener); } else { - authorizeAction(authentication, action, requestId, unwrappedRequest, authzInfo, listener); + authorizeAction(requestInfo, requestId, authzInfo, listener); } } - private void authorizeAction(final Authentication authentication, final String action, final String requestId, - final TransportRequest unwrappedRequest, final AuthorizationInfo authzInfo, + private void authorizeAction(final RequestInfo requestInfo, final String requestId, final AuthorizationInfo authzInfo, final ActionListener listener) { + final Authentication authentication = requestInfo.getAuthentication(); + final TransportRequest request = requestInfo.getRequest(); + final String action = requestInfo.getAction(); final AuthorizationEngine authzEngine = getAuthorizationEngine(authentication); if (ClusterPrivilege.ACTION_MATCHER.test(action)) { final ActionListener clusterAuthzListener = wrapPreservingContext(ActionListener.wrap(result -> { if (result.isGranted()) { if (result.isAuditable()) { - auditTrail.accessGranted(requestId, authentication, action, unwrappedRequest, authzInfo); + auditTrail.accessGranted(requestId, authentication, action, request, authzInfo); } putTransientIfNonExisting(AuthorizationServiceField.INDICES_PERMISSIONS_KEY, IndicesAccessControl.ALLOW_ALL); listener.onResponse(null); } else { - listener.onFailure(denial(requestId, authentication, action, unwrappedRequest, authzInfo)); + listener.onFailure(denial(requestId, authentication, action, request, authzInfo)); } }, e -> { // TODO need a failure handler better than this! - listener.onFailure(denial(requestId, authentication, action, unwrappedRequest, authzInfo, e)); + listener.onFailure(denial(requestId, authentication, action, request, authzInfo, e)); }), threadContext); - authzEngine.authorizeClusterAction(authentication, unwrappedRequest, action, authzInfo, clusterAuthzListener); + authzEngine.authorizeClusterAction(requestInfo, authzInfo, clusterAuthzListener); } else if (IndexPrivilege.ACTION_MATCHER.test(action)) { final MetaData metaData = clusterService.state().metaData(); final AsyncSupplier> authorizedIndicesSupplier = new CachingAsyncSupplier<>(authzIndicesListener -> - authzEngine.loadAuthorizedIndices(authentication, action, authzInfo, metaData.getAliasAndIndexLookup(), + authzEngine.loadAuthorizedIndices(requestInfo, authzInfo, metaData.getAliasAndIndexLookup(), authzIndicesListener)); final AsyncSupplier resolvedIndicesAsyncSupplier = new CachingAsyncSupplier<>((resolvedIndicesListener) -> { authorizedIndicesSupplier.get(ActionListener.wrap(authorizedIndices -> { - resolveIndexNames(unwrappedRequest, metaData, authorizedIndices, resolvedIndicesListener); + resolveIndexNames(request, metaData, authorizedIndices, resolvedIndicesListener); }, e -> { if (e instanceof IndexNotFoundException) { - auditTrail.accessDenied(requestId, authentication, action, unwrappedRequest, authzInfo); + auditTrail.accessDenied(requestId, authentication, action, request, authzInfo); listener.onFailure(e); } else { - listener.onFailure(denial(requestId, authentication, action, unwrappedRequest, authzInfo, e)); + listener.onFailure(denial(requestId, authentication, action, request, authzInfo, e)); } })); }); - authzEngine.authorizeIndexAction(authentication, unwrappedRequest, action, authzInfo, resolvedIndicesAsyncSupplier, + authzEngine.authorizeIndexAction(requestInfo, authzInfo, resolvedIndicesAsyncSupplier, metaData.getAliasAndIndexLookup()::get, ActionListener.wrap(indexAuthorizationResult -> { if (indexAuthorizationResult.isGranted()) { if (indexAuthorizationResult.getIndicesAccessControl() != null) { @@ -225,10 +232,11 @@ private void authorizeAction(final Authentication authentication, final String a } //if we are creating an index we need to authorize potential aliases created at the same time if (IndexPrivilege.CREATE_INDEX_MATCHER.test(action)) { - assert unwrappedRequest instanceof CreateIndexRequest; - Set aliases = ((CreateIndexRequest) unwrappedRequest).aliases(); + assert request instanceof CreateIndexRequest; + Set aliases = ((CreateIndexRequest) request).aliases(); if (aliases.isEmpty() == false) { - authzEngine.authorizeIndexAction(authentication, unwrappedRequest, IndicesAliasesAction.NAME, authzInfo, + final RequestInfo aliasesRequestInfo = new RequestInfo(authentication, request, IndicesAliasesAction.NAME); + authzEngine.authorizeIndexAction(aliasesRequestInfo, authzInfo, ril -> { resolvedIndicesAsyncSupplier.get(ActionListener.wrap(resolvedIndices -> { List aliasesAndIndices = new ArrayList<>(resolvedIndices.getLocal()); @@ -243,15 +251,15 @@ private void authorizeAction(final Authentication authentication, final String a if (authorizationResult.isGranted()) { if (authorizationResult.isAuditable()) { auditTrail.accessGranted(requestId, authentication, IndicesAliasesAction.NAME, - unwrappedRequest, authzInfo); + request, authzInfo); } if (indexAuthorizationResult.isAuditable()) { - auditTrail.accessGranted(requestId, authentication, action, unwrappedRequest, authzInfo); + auditTrail.accessGranted(requestId, authentication, action, request, authzInfo); } listener.onResponse(null); } else { listener.onFailure(denial(requestId, authentication, IndicesAliasesAction.NAME, - unwrappedRequest, authzInfo)); + request, authzInfo)); } }, listener::onFailure)); } else { @@ -259,29 +267,28 @@ private void authorizeAction(final Authentication authentication, final String a } } else if (action.equals(TransportShardBulkAction.ACTION_NAME)) { // if this is performing multiple actions on the index, then check each of those actions. - assert unwrappedRequest instanceof BulkShardRequest - : "Action " + action + " requires " + BulkShardRequest.class + " but was " + unwrappedRequest.getClass(); + assert request instanceof BulkShardRequest + : "Action " + action + " requires " + BulkShardRequest.class + " but was " + request.getClass(); - authorizeBulkItems(authentication, (BulkShardRequest) unwrappedRequest, authzInfo, authzEngine, - resolvedIndicesAsyncSupplier, authorizedIndicesSupplier, metaData, requestId, - ActionListener.wrap(ignore -> { + authorizeBulkItems(requestInfo, authzInfo, authzEngine, resolvedIndicesAsyncSupplier, authorizedIndicesSupplier, + metaData, requestId, ActionListener.wrap(ignore -> { if (indexAuthorizationResult.isAuditable()) { - auditTrail.accessGranted(requestId, authentication, action, unwrappedRequest, authzInfo); + auditTrail.accessGranted(requestId, authentication, action, request, authzInfo); } listener.onResponse(null); }, listener::onFailure)); } else { if (indexAuthorizationResult.isAuditable()) { - auditTrail.accessGranted(requestId, authentication, action, unwrappedRequest, authzInfo); + auditTrail.accessGranted(requestId, authentication, action, request, authzInfo); } listener.onResponse(null); } } else { - listener.onFailure(denial(requestId, authentication, action, unwrappedRequest, authzInfo)); + listener.onFailure(denial(requestId, authentication, action, request, authzInfo)); } }, listener::onFailure)); } else { - listener.onFailure(denial(requestId, authentication, action, unwrappedRequest, authzInfo)); + listener.onFailure(denial(requestId, authentication, action, request, authzInfo)); } } @@ -336,16 +343,18 @@ private boolean isInternalUser(User user) { return SystemUser.is(user) || XPackUser.is(user) || XPackSecurityUser.is(user); } - private void authorizeRunAs(final Authentication authentication, final String action, final String requestId, - final TransportRequest request, final AuthorizationInfo authzInfo, + private void authorizeRunAs(final RequestInfo requestInfo, final String requestId, final AuthorizationInfo authzInfo, final ActionListener listener) { + final Authentication authentication = requestInfo.getAuthentication(); + final TransportRequest request = requestInfo.getRequest(); + final String action = requestInfo.getAction(); if (authentication.getLookedUpBy() == null) { // this user did not really exist // TODO(jaymode) find a better way to indicate lookup failed for a user and we need to fail authz throw denyRunAs(requestId, authentication, action, request, authzInfo.getAuthenticatedUserAuthorizationInfo()); } else { final AuthorizationEngine runAsAuthzEngine = getRunAsAuthorizationEngine(authentication); - runAsAuthzEngine.authorizeRunAs(authentication, request, action, authzInfo, listener); + runAsAuthzEngine.authorizeRunAs(requestInfo, authzInfo, listener); } } @@ -363,10 +372,12 @@ private void authorizeRunAs(final Authentication authentication, final String ac * is very small, but the results must be cached, to avoid adding a high * overhead to each bulk request. */ - private void authorizeBulkItems(Authentication authentication, BulkShardRequest request, AuthorizationInfo authzInfo, + private void authorizeBulkItems(RequestInfo requestInfo, AuthorizationInfo authzInfo, AuthorizationEngine authzEngine, AsyncSupplier resolvedIndicesAsyncSupplier, AsyncSupplier> authorizedIndicesSupplier, MetaData metaData, String requestId, ActionListener listener) { + final Authentication authentication = requestInfo.getAuthentication(); + final BulkShardRequest request = (BulkShardRequest) requestInfo.getRequest(); // Maps original-index -> expanded-index-name (expands date-math, but not aliases) final Map resolvedIndexNames = new HashMap<>(); // Maps action -> resolved indices set @@ -436,7 +447,9 @@ private void authorizeBulkItems(Authentication authentication, BulkShardRequest new GroupedActionListener<>(bulkAuthzListener, actionToIndicesMap.size(), Collections.emptyList()), threadContext); actionToIndicesMap.forEach((bulkItemAction, indices) -> { - authzEngine.authorizeIndexAction(authentication, request, bulkItemAction, authzInfo, + final RequestInfo bulkItemInfo = + new RequestInfo(requestInfo.getAuthentication(), requestInfo.getRequest(), bulkItemAction); + authzEngine.authorizeIndexAction(bulkItemInfo, authzInfo, ril -> ril.onResponse(new ResolvedIndices(new ArrayList<>(indices), Collections.emptyList())), metaData.getAliasAndIndexLookup()::get, ActionListener.wrap(indexAuthorizationResult -> groupedActionListener.onResponse(new Tuple<>(bulkItemAction, indexAuthorizationResult)), diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/RBACEngine.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/RBACEngine.java index 5c36106bab6cb..861975bcf4d6f 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/RBACEngine.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/RBACEngine.java @@ -78,8 +78,8 @@ public class RBACEngine implements AuthorizationEngine { } @Override - public void resolveAuthorizationInfo(Authentication authentication, TransportRequest request, String action, - ActionListener listener) { + public void resolveAuthorizationInfo(RequestInfo requestInfo, ActionListener listener) { + final Authentication authentication = requestInfo.getAuthentication(); getRoles(authentication.getUser(), ActionListener.wrap(role -> { if (authentication.getUser().isRunAs()) { getRoles(authentication.getUser().authenticatedUser(), ActionListener.wrap( @@ -96,11 +96,11 @@ private void getRoles(User user, ActionListener listener) { } @Override - public void authorizeRunAs(Authentication authentication, TransportRequest request, String action, AuthorizationInfo authorizationInfo, - ActionListener listener) { + public void authorizeRunAs(RequestInfo requestInfo, AuthorizationInfo authorizationInfo, ActionListener listener) { if (authorizationInfo instanceof RBACAuthorizationInfo) { final Role role = ((RBACAuthorizationInfo) authorizationInfo).getAuthenticatedUserAuthorizationInfo().getRole(); - listener.onResponse(new AuthorizationResult(role.runAs().check(authentication.getUser().principal()))); + listener.onResponse( + new AuthorizationResult(role.runAs().check(requestInfo.getAuthentication().getUser().principal()))); } else { listener.onFailure(new IllegalArgumentException("unsupported authorization info:" + authorizationInfo.getClass().getSimpleName())); @@ -108,13 +108,13 @@ public void authorizeRunAs(Authentication authentication, TransportRequest reque } @Override - public void authorizeClusterAction(Authentication authentication, TransportRequest request, String action, - AuthorizationInfo authorizationInfo, ActionListener listener) { + public void authorizeClusterAction(RequestInfo requestInfo, AuthorizationInfo authorizationInfo, + ActionListener listener) { if (authorizationInfo instanceof RBACAuthorizationInfo) { final Role role = ((RBACAuthorizationInfo) authorizationInfo).getRole(); - if (role.cluster().check(action, request)) { + if (role.cluster().check(requestInfo.getAction(), requestInfo.getRequest())) { listener.onResponse(AuthorizationResult.granted()); - } else if (checkSameUserPermissions(action, request, authentication)) { + } else if (checkSameUserPermissions(requestInfo.getAction(), requestInfo.getRequest(), requestInfo.getAuthentication())) { listener.onResponse(AuthorizationResult.granted()); } else { listener.onResponse(AuthorizationResult.deny()); @@ -186,10 +186,13 @@ private static boolean shouldAuthorizeIndexActionNameOnly(String action, Transpo } @Override - public void authorizeIndexAction(Authentication authentication, TransportRequest request, String action, - AuthorizationInfo authorizationInfo, AsyncSupplier indicesAsyncSupplier, + public void authorizeIndexAction(RequestInfo requestInfo, AuthorizationInfo authorizationInfo, + AsyncSupplier indicesAsyncSupplier, Function aliasOrIndexFunction, ActionListener listener) { + final String action = requestInfo.getAction(); + final TransportRequest request = requestInfo.getRequest(); + final Authentication authentication = requestInfo.getAuthentication(); if (TransportActionProxy.isProxyAction(action) || shouldAuthorizeIndexActionNameOnly(action, request)) { // we've already validated that the request is a proxy request so we can skip that but we still // need to validate that the action is allowed and then move on @@ -278,11 +281,11 @@ private void authorizeIndexActionName(String action, AuthorizationInfo authoriza } @Override - public void loadAuthorizedIndices(Authentication authentication, String action, AuthorizationInfo authorizationInfo, + public void loadAuthorizedIndices(RequestInfo requestInfo, AuthorizationInfo authorizationInfo, Map aliasAndIndexLookup, ActionListener> listener) { if (authorizationInfo instanceof RBACAuthorizationInfo) { final Role role = ((RBACAuthorizationInfo) authorizationInfo).getRole(); - listener.onResponse(resolveAuthorizedIndicesFromRole(role, action, aliasAndIndexLookup)); + listener.onResponse(resolveAuthorizedIndicesFromRole(role, requestInfo.getAction(), aliasAndIndexLookup)); } else { listener.onFailure( new IllegalArgumentException("unsupported authorization info:" + authorizationInfo.getClass().getSimpleName()));