Skip to content

Introduce asynchronous RBACEngine #36245

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 39 commits into from
Jan 7, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
30a8db0
authz engine start
jaymode Oct 19, 2018
4d1c18b
more progress
jaymode Oct 24, 2018
0111d9e
authz info
jaymode Oct 25, 2018
93b99eb
more work to authz cluster perms
jaymode Oct 26, 2018
4e7d86c
wip
jaymode Nov 1, 2018
637ac2c
more wip
jaymode Nov 13, 2018
8a80bb7
getting there
jaymode Nov 16, 2018
585ad75
good amount working but still need tests passing and address nocommits
jaymode Nov 16, 2018
f04a6ca
Merge branch 'master' into authz_engine
jaymode Nov 26, 2018
e28402e
Merge branch 'master' into authz_engine
jaymode Nov 28, 2018
739f01e
audit authz info wip
jaymode Nov 29, 2018
5b62936
Merge branch 'master' into authz_engine
jaymode Nov 30, 2018
9f1030b
authz info fix compile and precommit
jaymode Dec 3, 2018
780c162
fix authorization service tests
jaymode Dec 3, 2018
d4c6738
fix tests
jaymode Dec 3, 2018
25342c6
fix tests with hack for request interceptors
jaymode Dec 4, 2018
d2893fa
test fixing
jaymode Dec 4, 2018
1dd074f
Merge branch 'master' into authz_engine
jaymode Dec 4, 2018
e0a08bb
nocommits to fixme
jaymode Dec 4, 2018
53f4515
remove unused import
jaymode Dec 4, 2018
4bc0cb9
unused import
jaymode Dec 4, 2018
fca0604
fix test
jaymode Dec 4, 2018
66dd977
Merge branch 'security_authz_engine' into authz_engine
jaymode Dec 5, 2018
437adbf
Merge branch 'security_authz_engine' into authz_engine
jaymode Dec 6, 2018
6a3c2fa
remove request from load authz indices
jaymode Dec 10, 2018
54c33bd
call same user permission check from authz service
jaymode Dec 10, 2018
d534beb
Merge branch 'security_authz_engine' into authz_engine
jaymode Dec 10, 2018
e267d3d
xpack security user should be included in system access granted
jaymode Dec 11, 2018
22cbc77
Merge branch 'security_authz_engine' into authz_engine
jaymode Dec 11, 2018
080d55f
Merge branch 'security_authz_engine' into authz_engine
jaymode Dec 20, 2018
cdd8f32
Revert "call same user permission check from authz service"
jaymode Dec 20, 2018
8b9ea88
keep same user permissions within the RBACEngine
jaymode Dec 20, 2018
7981b94
rename authorizePostRunAs to authorizeAction
jaymode Dec 20, 2018
147a64e
move more logic into RBACEngine
jaymode Dec 20, 2018
4394015
more refactoring and cleanups
jaymode Dec 21, 2018
5548ff8
fix precommit
jaymode Dec 21, 2018
8228d20
Merge branch 'security_authz_engine' into authz_engine
jaymode Jan 7, 2019
0e5e13a
reorder arguments
jaymode Jan 7, 2019
437c253
Merge branch 'security_authz_engine' into authz_engine
jaymode Jan 7, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class IndicesAccessControl {
public static final IndicesAccessControl ALLOW_NO_INDICES = new IndicesAccessControl(true,
Collections.singletonMap(IndicesAndAliasesResolverField.NO_INDEX_PLACEHOLDER,
new IndicesAccessControl.IndexAccessControl(true, new FieldPermissions(), null)));
public static final IndicesAccessControl DENIED = new IndicesAccessControl(false, Collections.emptyMap());

private final boolean granted;
private final Map<String, IndexAccessControl> indexPermissions;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.cluster.metadata.AliasOrIndex;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
Expand All @@ -28,7 +27,6 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
Expand Down Expand Up @@ -152,19 +150,18 @@ public Automaton allowedActionsMatcher(String index) {
* Authorizes the provided action against the provided indices, given the current cluster metadata
*/
public Map<String, IndicesAccessControl.IndexAccessControl> authorize(String action, Set<String> requestedIndicesOrAliases,
MetaData metaData, FieldPermissionsCache fieldPermissionsCache) {
Function<String, AliasOrIndex> allAliasesAndIndices,
FieldPermissionsCache fieldPermissionsCache) {
// now... every index that is associated with the request, must be granted
// by at least one indices permission group

SortedMap<String, AliasOrIndex> allAliasesAndIndices = metaData.getAliasAndIndexLookup();
Map<String, Set<FieldPermissions>> fieldPermissionsByIndex = new HashMap<>();
Map<String, DocumentLevelPermissions> roleQueriesByIndex = new HashMap<>();
Map<String, Boolean> grantedBuilder = new HashMap<>();

for (String indexOrAlias : requestedIndicesOrAliases) {
boolean granted = false;
Set<String> concreteIndices = new HashSet<>();
AliasOrIndex aliasOrIndex = allAliasesAndIndices.get(indexOrAlias);
AliasOrIndex aliasOrIndex = allAliasesAndIndices.apply(indexOrAlias);
if (aliasOrIndex != null) {
for (IndexMetaData indexMetaData : aliasOrIndex.getIndices()) {
concreteIndices.add(indexMetaData.getIndex().getName());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/
package org.elasticsearch.xpack.core.security.authz.permission;

import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.metadata.AliasOrIndex;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.Tuple;
Expand All @@ -25,6 +25,7 @@
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;

public final class Role {

Expand Down Expand Up @@ -77,10 +78,11 @@ public static Builder builder(RoleDescriptor rd, FieldPermissionsCache fieldPerm
* specified action with the requested indices/aliases. At the same time if field and/or document level security
* is configured for any group also the allowed fields and role queries are resolved.
*/
public IndicesAccessControl authorize(String action, Set<String> requestedIndicesOrAliases, MetaData metaData,
public IndicesAccessControl authorize(String action, Set<String> requestedIndicesOrAliases,
Function<String, AliasOrIndex> aliasAndIndexLookup,
FieldPermissionsCache fieldPermissionsCache) {
Map<String, IndicesAccessControl.IndexAccessControl> indexPermissions = indices.authorize(
action, requestedIndicesOrAliases, metaData, fieldPermissionsCache
action, requestedIndicesOrAliases, aliasAndIndexLookup, fieldPermissionsCache
);

// At least one role / indices permission set need to match with all the requested indices/aliases:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ public static ElasticsearchSecurityException authenticationError(String msg, Obj
}

public static ElasticsearchSecurityException authorizationError(String msg, Object... args) {
return new ElasticsearchSecurityException(msg, RestStatus.FORBIDDEN, args);
return new ElasticsearchSecurityException(msg, RestStatus.FORBIDDEN, null, args);
}

public static ElasticsearchSecurityException authorizationError(String msg, Exception cause, Object... args) {
return new ElasticsearchSecurityException(msg, RestStatus.FORBIDDEN, cause, args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import org.elasticsearch.action.search.SearchAction;
import org.elasticsearch.action.update.UpdateAction;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.AliasOrIndex;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.settings.Settings;
Expand Down Expand Up @@ -118,6 +119,7 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.SortedMap;

import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.is;
Expand Down Expand Up @@ -587,18 +589,20 @@ public void testSuperuserRole() {
.build();

FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY);
SortedMap<String, AliasOrIndex> lookup = metaData.getAliasAndIndexLookup();
Map<String, IndexAccessControl> authzMap =
superuserRole.indices().authorize(SearchAction.NAME, Sets.newHashSet("a1", "ba"), metaData, fieldPermissionsCache);
superuserRole.indices().authorize(SearchAction.NAME, Sets.newHashSet("a1", "ba"), lookup::get, fieldPermissionsCache);
assertThat(authzMap.get("a1").isGranted(), is(true));
assertThat(authzMap.get("b").isGranted(), is(true));
authzMap = superuserRole.indices().authorize(DeleteIndexAction.NAME, Sets.newHashSet("a1", "ba"), metaData, fieldPermissionsCache);
authzMap =
superuserRole.indices().authorize(DeleteIndexAction.NAME, Sets.newHashSet("a1", "ba"), lookup::get, fieldPermissionsCache);
assertThat(authzMap.get("a1").isGranted(), is(true));
assertThat(authzMap.get("b").isGranted(), is(true));
authzMap = superuserRole.indices().authorize(IndexAction.NAME, Sets.newHashSet("a2", "ba"), metaData, fieldPermissionsCache);
authzMap = superuserRole.indices().authorize(IndexAction.NAME, Sets.newHashSet("a2", "ba"), lookup::get, fieldPermissionsCache);
assertThat(authzMap.get("a2").isGranted(), is(true));
assertThat(authzMap.get("b").isGranted(), is(true));
authzMap = superuserRole.indices()
.authorize(UpdateSettingsAction.NAME, Sets.newHashSet("aaaaaa", "ba"), metaData, fieldPermissionsCache);
.authorize(UpdateSettingsAction.NAME, Sets.newHashSet("aaaaaa", "ba"), lookup::get, fieldPermissionsCache);
assertThat(authzMap.get("aaaaaa").isGranted(), is(true));
assertThat(authzMap.get("b").isGranted(), is(true));
assertTrue(superuserRole.indices().check(SearchAction.NAME));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,17 @@
import org.elasticsearch.xpack.core.XPackField;
import org.elasticsearch.xpack.core.security.SecurityContext;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authz.permission.Role;
import org.elasticsearch.xpack.core.security.authz.privilege.HealthAndStatsPrivilege;
import org.elasticsearch.xpack.core.security.support.Automatons;
import org.elasticsearch.xpack.core.security.user.SystemUser;
import org.elasticsearch.xpack.security.action.SecurityActionMapper;
import org.elasticsearch.xpack.security.action.interceptor.RequestInterceptor;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.authz.AuthorizationEngine.AuthorizationInfo;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.authz.AuthorizationUtils;
import org.elasticsearch.xpack.security.authz.RBACEngine.RBACAuthorizationInfo;

import java.io.IOException;
import java.util.Set;
Expand Down Expand Up @@ -164,21 +167,24 @@ private <Request extends ActionRequest> void authorizeRequest(Authentication aut
if (authentication == null) {
listener.onFailure(new IllegalArgumentException("authentication must be non null for authorization"));
} else {
final AuthorizationUtils.AsyncAuthorizer asyncAuthorizer = new AuthorizationUtils.AsyncAuthorizer(authentication, listener,
(userRoles, runAsRoles) -> {
authzService.authorize(authentication, securityAction, request, userRoles, runAsRoles);
/*
* We use a separate concept for code that needs to be run after authentication and authorization that could
* affect the running of the action. This is done to make it more clear of the state of the request.
*/
for (RequestInterceptor interceptor : requestInterceptors) {
if (interceptor.supports(request)) {
interceptor.intercept(request, authentication, runAsRoles != null ? runAsRoles : userRoles, securityAction);
}
}
listener.onResponse(null);
});
asyncAuthorizer.authorize(authzService);
authzService.authorize(authentication, securityAction, request, ActionListener.wrap(ignore -> {
/*
* We use a separate concept for code that needs to be run after authentication and authorization that could
* affect the running of the action. This is done to make it more clear of the state of the request.
*/
// FIXME this needs to be done in a way that allows us to operate without a role
Role role = null;
AuthorizationInfo authorizationInfo = threadContext.getTransient("_authz_info");
if (authorizationInfo instanceof RBACAuthorizationInfo) {
role = ((RBACAuthorizationInfo) authorizationInfo).getRole();
}
for (RequestInterceptor interceptor : requestInterceptors) {
if (interceptor.supports(request)) {
interceptor.intercept(request, authentication, role, securityAction);
}
}
listener.onResponse(null);
}, listener::onFailure));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import java.util.HashMap;
import java.util.Map;

import static org.elasticsearch.xpack.security.authz.AuthorizationService.AUTHORIZATION_INFO_KEY;

public final class IndicesAliasesRequestInterceptor implements RequestInterceptor<IndicesAliasesRequest> {

private final ThreadContext threadContext;
Expand Down Expand Up @@ -72,7 +74,7 @@ public void intercept(IndicesAliasesRequest request, Authentication authenticati
if (Operations.subsetOf(aliasPermissions, indexPermissions) == false) {
// TODO we've already audited a access granted event so this is going to look ugly
auditTrailService.accessDenied(AuditUtil.extractRequestId(threadContext), authentication, action, request,
userPermissions.names());
threadContext.getTransient(AUTHORIZATION_INFO_KEY));
throw Exceptions.authorizationError("Adding an alias is not allowed when the alias " +
"has more permissions than any of the indices");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.elasticsearch.xpack.security.audit.AuditTrailService;

import static org.elasticsearch.xpack.security.audit.AuditUtil.extractRequestId;
import static org.elasticsearch.xpack.security.authz.AuthorizationService.AUTHORIZATION_INFO_KEY;

public final class ResizeRequestInterceptor implements RequestInterceptor<ResizeRequest> {

Expand Down Expand Up @@ -61,7 +62,8 @@ public void intercept(ResizeRequest request, Authentication authentication, Role
userPermissions.indices().allowedActionsMatcher(request.getTargetIndexRequest().index());
if (Operations.subsetOf(targetIndexPermissions, sourceIndexPermissions) == false) {
// TODO we've already audited a access granted event so this is going to look ugly
auditTrailService.accessDenied(extractRequestId(threadContext), authentication, action, request, userPermissions.names());
auditTrailService.accessDenied(extractRequestId(threadContext), authentication, action, request,
threadContext.getTransient(AUTHORIZATION_INFO_KEY));
throw Exceptions.authorizationError("Resizing an index is not allowed when the target index " +
"has more permissions than the source index");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
Expand All @@ -21,6 +22,7 @@
import org.elasticsearch.xpack.core.security.action.user.GetUserPrivilegesResponse;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsCache;
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsDefinition;
import org.elasticsearch.xpack.core.security.authz.permission.IndicesPermission;
import org.elasticsearch.xpack.core.security.authz.permission.Role;
Expand All @@ -29,7 +31,7 @@
import org.elasticsearch.xpack.core.security.authz.privilege.ConditionalClusterPrivilege;
import org.elasticsearch.xpack.core.security.authz.privilege.Privilege;
import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;

import java.util.Arrays;
import java.util.Collections;
Expand All @@ -46,14 +48,14 @@
public class TransportGetUserPrivilegesAction extends HandledTransportAction<GetUserPrivilegesRequest, GetUserPrivilegesResponse> {

private final ThreadPool threadPool;
private final AuthorizationService authorizationService;
private final CompositeRolesStore rolesStore;

@Inject
public TransportGetUserPrivilegesAction(ThreadPool threadPool, TransportService transportService,
ActionFilters actionFilters, AuthorizationService authorizationService) {
ActionFilters actionFilters, CompositeRolesStore rolesStore) {
super(GetUserPrivilegesAction.NAME, transportService, actionFilters, GetUserPrivilegesRequest::new);
this.threadPool = threadPool;
this.authorizationService = authorizationService;
this.rolesStore = rolesStore;
}

@Override
Expand All @@ -66,7 +68,8 @@ protected void doExecute(Task task, GetUserPrivilegesRequest request, ActionList
return;
}

authorizationService.roles(user, ActionListener.wrap(
// FIXME reuse field permissions cache!
rolesStore.getRoles(user, new FieldPermissionsCache(Settings.EMPTY), ActionListener.wrap(
role -> listener.onResponse(buildResponseObject(role)),
listener::onFailure));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.elasticsearch.action.support.HandledTransportAction;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.tasks.Task;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
Expand All @@ -21,6 +22,7 @@
import org.elasticsearch.xpack.core.security.action.user.HasPrivilegesResponse;
import org.elasticsearch.xpack.core.security.authc.Authentication;
import org.elasticsearch.xpack.core.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.core.security.authz.permission.FieldPermissionsCache;
import org.elasticsearch.xpack.core.security.authz.permission.IndicesPermission;
import org.elasticsearch.xpack.core.security.authz.permission.Role;
import org.elasticsearch.xpack.core.security.authz.privilege.ApplicationPrivilege;
Expand All @@ -30,7 +32,7 @@
import org.elasticsearch.xpack.core.security.authz.privilege.Privilege;
import org.elasticsearch.xpack.core.security.support.Automatons;
import org.elasticsearch.xpack.core.security.user.User;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.xpack.security.authz.store.NativePrivilegeStore;

import java.util.ArrayList;
Expand All @@ -51,16 +53,16 @@
public class TransportHasPrivilegesAction extends HandledTransportAction<HasPrivilegesRequest, HasPrivilegesResponse> {

private final ThreadPool threadPool;
private final AuthorizationService authorizationService;
private final CompositeRolesStore rolesStore;
private final NativePrivilegeStore privilegeStore;

@Inject
public TransportHasPrivilegesAction(ThreadPool threadPool, TransportService transportService,
ActionFilters actionFilters, AuthorizationService authorizationService,
ActionFilters actionFilters, CompositeRolesStore rolesStore,
NativePrivilegeStore privilegeStore) {
super(HasPrivilegesAction.NAME, transportService, actionFilters, HasPrivilegesRequest::new);
this.threadPool = threadPool;
this.authorizationService = authorizationService;
this.rolesStore = rolesStore;
this.privilegeStore = privilegeStore;
}

Expand All @@ -74,7 +76,8 @@ protected void doExecute(Task task, HasPrivilegesRequest request, ActionListener
return;
}

authorizationService.roles(user, ActionListener.wrap(
// FIXME
rolesStore.getRoles(user, new FieldPermissionsCache(Settings.EMPTY), ActionListener.wrap(
role -> resolveApplicationPrivileges(request, ActionListener.wrap(
applicationPrivilegeLookup -> checkPrivileges(request, role, applicationPrivilegeLookup, listener),
listener::onFailure)),
Expand Down
Loading