Build a single role that represents a user's permissions (elastic/elasticsearch#4449)

This PR changes how we use roles and how we look at the roles of a user. Previously we looked up each role individually, parsed each into their own `Role` object, and had a wrapper that essentially served as an iterator over the roles. The same pattern was also followed for the permissions that composed a role (ClusterPermission, IndicesPermission, and RunAsPermission). This resulted in a lot of code that was hard to follow and could be inefficient. 

Now, we look up the roles for a user in bulk and only get the RoleDescriptor for each role. Once all role descriptors have been retrieved, we build a single Role that represents the user's permissions and we also cache this combination for better performance as authorization can happen many times for a single top level request as we authorize the top level request and any sub requests, which could be a large number in the case of shard requests.

This change also enabled a large cleanup of our permission and privilege classes, which should reduce the footprint of what needs to be followed. Some of the notable changes are:

* Consolidation of GeneralPrivilege and AbstractAutomatonPrivilege into the Privilege class
* The DefaultRole class has been removed and the permissions it provided were moved into the AuthorizationService
* The GlobalPermission class was removed as there is a single role that represents a user's permissions
* The Global inner classes for the various permissions were removed
* The Core inner class was removed and ClusterPermission, IndexPermission, RunAsPermission became final classes instead of interfaces
* The Permission interface has been removed. The isEmpty() method defined by this interface is not needed as we can simply evaluate the permission to get the same effect
* The ClusterPermission#check method only takes the action name again
* The AutomatonPredicate class was removed and replaced by Automatons#predicate
* IndicesAccessControl objects no longer need to be merged when evaluating permissions
* MergedFieldPermissions has been removed
* The Name class that was used to hold an array of strings has been removed and replaced with the use of a Set
* Privilege resolution is more efficient by only combining automata once

Other items:
* NativeRolesStore no longer does caching, so the RoleAndVersion class could be removed
* FileRolesStore doesn't need to be an AbstractLifecycleComponent

Relates elastic/elasticsearch#4327

Original commit: elastic/x-pack-elasticsearch@c1901bc82e
This commit is contained in:
Jay Modi 2016-12-30 09:27:49 -05:00 committed by GitHub
parent 9c09b88e9f
commit 18a2cf23d4
80 changed files with 2284 additions and 3665 deletions

View File

@ -96,6 +96,7 @@ import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.authz.accesscontrol.OptOutQueryCache;
import org.elasticsearch.xpack.security.authz.accesscontrol.SecurityIndexSearcherWrapper;
import org.elasticsearch.xpack.security.authz.accesscontrol.SetSecurityUserProcessor;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.xpack.security.authz.store.FileRolesStore;
import org.elasticsearch.xpack.security.authz.store.NativeRolesStore;
@ -318,10 +319,9 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
final CompositeRolesStore allRolesStore = new CompositeRolesStore(settings, fileRolesStore, nativeRolesStore, reservedRolesStore);
final AuthorizationService authzService = new AuthorizationService(settings, allRolesStore, clusterService,
auditTrailService, failureHandler, threadPool, anonymousUser);
components.add(fileRolesStore); // has lifecycle
components.add(nativeRolesStore); // used by roles actions
components.add(reservedRolesStore); // used by roles actions
components.add(allRolesStore); // for SecurityFeatureSet
components.add(allRolesStore); // for SecurityFeatureSet and clear roles cache
components.add(authzService);
components.add(new SecurityLifecycleService(settings, clusterService, threadPool, indexAuditTrail,
@ -404,6 +404,8 @@ public class Security implements ActionPlugin, IngestPlugin, NetworkPlugin {
NativeRolesStore.addSettings(settingsList);
AuthenticationService.addSettings(settingsList);
AuthorizationService.addSettings(settingsList);
settingsList.add(CompositeRolesStore.CACHE_SIZE_SETTING);
settingsList.add(FieldPermissionsCache.CACHE_SIZE_SETTING);
// encryption settings
CryptoService.addSettings(settingsList);

View File

@ -37,9 +37,9 @@ import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.authz.AuthorizationUtils;
import org.elasticsearch.xpack.security.authz.privilege.GeneralPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.HealthAndStatsPrivilege;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.elasticsearch.xpack.security.support.Automatons;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
@ -55,8 +55,7 @@ import static org.elasticsearch.xpack.security.support.Exceptions.authorizationE
public class SecurityActionFilter extends AbstractComponent implements ActionFilter {
private static final Predicate<String> LICENSE_EXPIRATION_ACTION_MATCHER = HealthAndStatsPrivilege.INSTANCE.predicate();
private static final Predicate<String> SECURITY_ACTION_MATCHER =
new GeneralPrivilege("_security_matcher", "cluster:admin/xpack/security*").predicate();
private static final Predicate<String> SECURITY_ACTION_MATCHER = Automatons.predicate("cluster:admin/xpack/security*");
private final AuthenticationService authcService;
private final AuthorizationService authzService;

View File

@ -15,7 +15,6 @@ import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.support.MetadataUtils;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
import java.io.IOException;
import java.util.ArrayList;
@ -65,12 +64,13 @@ public class PutRoleRequest extends ActionRequest implements WriteRequest<PutRol
this.indicesPrivileges.addAll(Arrays.asList(privileges));
}
public void addIndex(String[] indices, String[] privileges, FieldPermissions fieldPermissions,
public void addIndex(String[] indices, String[] privileges, String[] grantedFields, String[] deniedFields,
@Nullable BytesReference query) {
this.indicesPrivileges.add(RoleDescriptor.IndicesPrivileges.builder()
.indices(indices)
.privileges(privileges)
.fieldPermissions(fieldPermissions)
.grantedFields(grantedFields)
.deniedFields(deniedFields)
.query(query)
.build());
}

View File

@ -11,7 +11,6 @@ import org.elasticsearch.client.ElasticsearchClient;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
import java.io.IOException;
import java.util.Map;
@ -58,9 +57,9 @@ public class PutRoleRequestBuilder extends ActionRequestBuilder<PutRoleRequest,
return this;
}
public PutRoleRequestBuilder addIndices(String[] indices, String[] privileges,
FieldPermissions fieldPermissions, @Nullable BytesReference query) {
request.addIndex(indices, privileges, fieldPermissions, query);
public PutRoleRequestBuilder addIndices(String[] indices, String[] privileges, String[] grantedFields, String[] deniedFields,
@Nullable BytesReference query) {
request.addIndex(indices, privileges, grantedFields, deniedFields, query);
return this;
}

View File

@ -12,7 +12,7 @@ import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.authz.store.NativeRolesStore;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
@ -21,12 +21,12 @@ import java.util.List;
public class TransportClearRolesCacheAction extends TransportNodesAction<ClearRolesCacheRequest, ClearRolesCacheResponse,
ClearRolesCacheRequest.Node, ClearRolesCacheResponse.Node> {
private final NativeRolesStore rolesStore;
private final CompositeRolesStore rolesStore;
@Inject
public TransportClearRolesCacheAction(Settings settings, ThreadPool threadPool,
ClusterService clusterService, TransportService transportService, ActionFilters actionFilters,
NativeRolesStore rolesStore, IndexNameExpressionResolver indexNameExpressionResolver) {
CompositeRolesStore rolesStore, IndexNameExpressionResolver indexNameExpressionResolver) {
super(settings, ClearRolesCacheAction.NAME, threadPool, clusterService, transportService,
actionFilters, indexNameExpressionResolver, ClearRolesCacheRequest::new, ClearRolesCacheRequest.Node::new,
ThreadPool.Names.MANAGEMENT, ClearRolesCacheResponse.Node.class);

View File

@ -5,8 +5,6 @@
*/
package org.elasticsearch.xpack.security.action.role;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.util.Supplier;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.action.support.HandledTransportAction;
@ -16,16 +14,12 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.KibanaRole;
import org.elasticsearch.xpack.security.authz.permission.LogstashSystemRole;
import org.elasticsearch.xpack.security.authz.store.NativeRolesStore;
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
import java.util.ArrayList;
import java.util.List;
import static org.elasticsearch.common.Strings.arrayToDelimitedString;
public class TransportGetRolesAction extends HandledTransportAction<GetRolesRequest, GetRolesResponse> {
private final NativeRolesStore nativeRolesStore;
@ -53,8 +47,12 @@ public class TransportGetRolesAction extends HandledTransportAction<GetRolesRequ
for (String role : requestedRoles) {
if (ReservedRolesStore.isReserved(role)) {
RoleDescriptor rd = reservedRolesStore.roleDescriptor(role);
assert rd != null : "No descriptor for role " + role;
roles.add(rd);
if (rd != null) {
roles.add(rd);
} else {
listener.onFailure(new IllegalStateException("unable to obtain reserved role [" + role + "]"));
return;
}
} else {
rolesToSearchFor.add(role);
}

View File

@ -31,28 +31,30 @@ import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.common.GroupedActionListener;
import org.elasticsearch.xpack.security.SecurityTemplateService;
import org.elasticsearch.xpack.security.action.user.AuthenticateAction;
import org.elasticsearch.xpack.security.action.user.ChangePasswordAction;
import org.elasticsearch.xpack.security.action.user.UserRequest;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.AuthenticationFailureHandler;
import org.elasticsearch.xpack.security.authc.esnative.NativeRealm;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.xpack.security.authz.permission.ClusterPermission;
import org.elasticsearch.xpack.security.authz.permission.DefaultRole;
import org.elasticsearch.xpack.security.authz.permission.GlobalPermission;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.permission.RunAsPermission;
import org.elasticsearch.xpack.security.authz.permission.SuperuserRole;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
import org.elasticsearch.xpack.security.support.Automatons;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.user.XPackUser;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
@ -70,6 +72,7 @@ public class AuthorizationService extends AbstractComponent {
public static final String ORIGINATING_ACTION_KEY = "_originating_action_name";
private static final Predicate<String> MONITOR_INDEX_PREDICATE = IndexPrivilege.MONITOR.predicate();
private static final Predicate<String> SAME_USER_PRIVILEGE = Automatons.predicate(ChangePasswordAction.NAME, AuthenticateAction.NAME);
private final ClusterService clusterService;
private final CompositeRolesStore rolesStore;
@ -78,6 +81,7 @@ public class AuthorizationService extends AbstractComponent {
private final AuthenticationFailureHandler authcFailureHandler;
private final ThreadContext threadContext;
private final AnonymousUser anonymousUser;
private final FieldPermissionsCache fieldPermissionsCache;
private final boolean isAnonymousEnabled;
private final boolean anonymousAuthzExceptionEnabled;
@ -94,6 +98,7 @@ public class AuthorizationService extends AbstractComponent {
this.anonymousUser = anonymousUser;
this.isAnonymousEnabled = AnonymousUser.isAnonymousEnabled(settings);
this.anonymousAuthzExceptionEnabled = ANONYMOUS_AUTHORIZATION_EXCEPTION_SETTING.get(settings);
this.fieldPermissionsCache = new FieldPermissionsCache(settings);
}
/**
@ -106,8 +111,8 @@ public class AuthorizationService extends AbstractComponent {
* @param request The request
* @throws ElasticsearchSecurityException If the given user is no allowed to execute the given request
*/
public void authorize(Authentication authentication, String action, TransportRequest request, Collection<Role> userRoles,
Collection<Role> runAsRoles) throws ElasticsearchSecurityException {
public void authorize(Authentication authentication, String action, TransportRequest request, Role userRole,
Role runAsRole) throws ElasticsearchSecurityException {
final TransportRequest originalRequest = request;
if (request instanceof ConcreteShardRequest) {
request = ((ConcreteShardRequest<?>) request).getRequest();
@ -124,38 +129,20 @@ public class AuthorizationService extends AbstractComponent {
}
throw denial(authentication, action, request);
}
Collection<Role> roles = userRoles;
// get the roles of the authenticated user, which may be different than the effective
GlobalPermission permission = permission(roles);
final boolean isRunAs = authentication.isRunAs();
// permission can be empty as it might be that the user's role is unknown
if (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(authentication, action, request);
} else {
throw denial(authentication, action, request);
}
}
// get the roles of the authenticated user, which may be different than the effective
Role permission = userRole;
// check if the request is a run as request
final boolean isRunAs = authentication.isRunAs();
if (isRunAs) {
// if we are running as a user we looked up then the authentication must contain a lookedUpBy. If it doesn't then this user
// doesn't really exist but the authc service allowed it through to avoid leaking users that exist in the system
if (authentication.getLookedUpBy() == null) {
throw denyRunAs(authentication, action, request);
}
// first we must authorize for the RUN_AS action
RunAsPermission runAs = permission.runAs();
if (runAs != null && runAs.check(authentication.getRunAsUser().principal())) {
} else if (permission.runAs().check(authentication.getRunAsUser().principal())) {
grantRunAs(authentication, action, request);
roles = runAsRoles;
permission = permission(roles);
// permission can be empty as it might be that the run as user's role is unknown
if (permission.isEmpty()) {
throw denial(authentication, action, request);
}
permission = runAsRole;
} else {
throw denyRunAs(authentication, action, request);
}
@ -164,8 +151,7 @@ public class AuthorizationService extends AbstractComponent {
// first, we'll check if the action is a cluster action. If it is, we'll only check it against the cluster permissions
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, authentication)) {
if (cluster.check(action) || checkSameUserPermissions(action, request, authentication)) {
setIndicesAccessControl(IndicesAccessControl.ALLOW_ALL);
grant(authentication, action, request);
return;
@ -210,12 +196,12 @@ public class AuthorizationService extends AbstractComponent {
throw denial(authentication, action, request);
}
if (permission.indices() == null || permission.indices().isEmpty()) {
if (permission.indices().check(action) == false) {
throw denial(authentication, action, request);
}
MetaData metaData = clusterService.state().metaData();
AuthorizedIndices authorizedIndices = new AuthorizedIndices(authentication.getRunAsUser(), roles, action, metaData);
AuthorizedIndices authorizedIndices = new AuthorizedIndices(authentication.getRunAsUser(), permission, action, metaData);
Set<String> indexNames = resolveIndexNames(authentication, action, request, metaData, authorizedIndices);
assert !indexNames.isEmpty() : "every indices request needs to have its indices set thus the resolved indices must not be empty";
@ -227,14 +213,14 @@ public class AuthorizationService extends AbstractComponent {
return;
}
IndicesAccessControl indicesAccessControl = permission.authorize(action, indexNames, metaData);
IndicesAccessControl indicesAccessControl = permission.authorize(action, indexNames, metaData, fieldPermissionsCache);
if (!indicesAccessControl.isGranted()) {
throw denial(authentication, action, request);
} else if (indicesAccessControl.getIndexPermissions(SecurityTemplateService.SECURITY_INDEX_NAME) != null
&& indicesAccessControl.getIndexPermissions(SecurityTemplateService.SECURITY_INDEX_NAME).isGranted()
&& XPackUser.is(authentication.getRunAsUser()) == false
&& MONITOR_INDEX_PREDICATE.test(action) == false
&& Arrays.binarySearch(authentication.getRunAsUser().roles(), SuperuserRole.NAME) < 0) {
&& Arrays.binarySearch(authentication.getRunAsUser().roles(), ReservedRolesStore.SUPERUSER_ROLE.name()) < 0) {
// 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 [{}]",
@ -253,7 +239,7 @@ public class AuthorizationService extends AbstractComponent {
for (Alias alias : aliases) {
aliasesAndIndices.add(alias.name());
}
indicesAccessControl = permission.authorize("indices:admin/aliases", aliasesAndIndices, metaData);
indicesAccessControl = permission.authorize("indices:admin/aliases", aliasesAndIndices, metaData, fieldPermissionsCache);
if (!indicesAccessControl.isGranted()) {
throw denial(authentication, "indices:admin/aliases", request);
}
@ -288,16 +274,7 @@ public class AuthorizationService extends AbstractComponent {
}
}
// pkg-private for testing
GlobalPermission permission(Collection<Role> roles) {
GlobalPermission.Compound.Builder rolesBuilder = GlobalPermission.Compound.builder();
for (Role role : roles) {
rolesBuilder.add(role);
}
return rolesBuilder.build();
}
public void roles(User user, ActionListener<Collection<Role>> roleActionListener) {
public void roles(User user, ActionListener<Role> roleActionListener) {
// we need to special case the internal users in this method, if we apply the anonymous roles to every user including these system
// user accounts then we run into the chance of a deadlock because then we need to get a role that we may be trying to get as the
// internal user. The SystemUser is special cased as it has special privileges to execute internal actions and should never be
@ -307,8 +284,8 @@ public class AuthorizationService extends AbstractComponent {
" roles");
}
if (XPackUser.is(user)) {
assert XPackUser.INSTANCE.roles().length == 1 && SuperuserRole.NAME.equals(XPackUser.INSTANCE.roles()[0]);
roleActionListener.onResponse(Collections.singleton(SuperuserRole.INSTANCE));
assert XPackUser.INSTANCE.roles().length == 1 && ReservedRolesStore.SUPERUSER_ROLE.name().equals(XPackUser.INSTANCE.roles()[0]);
roleActionListener.onResponse(ReservedRolesStore.SUPERUSER_ROLE);
return;
}
@ -321,15 +298,12 @@ public class AuthorizationService extends AbstractComponent {
Collections.addAll(roleNames, anonymousUser.roles());
}
final Collection<Role> defaultRoles = Collections.singletonList(DefaultRole.INSTANCE);
if (roleNames.isEmpty()) {
roleActionListener.onResponse(defaultRoles);
roleActionListener.onResponse(Role.EMPTY);
} else if (roleNames.contains(ReservedRolesStore.SUPERUSER_ROLE.name())) {
roleActionListener.onResponse(ReservedRolesStore.SUPERUSER_ROLE);
} else {
final GroupedActionListener<Role> listener = new GroupedActionListener<>(roleActionListener, roleNames.size(),
defaultRoles);
for (String roleName : roleNames) {
rolesStore.roles(roleName, listener);
}
rolesStore.roles(roleNames, fieldPermissionsCache, roleActionListener);
}
}
@ -354,6 +328,49 @@ public class AuthorizationService extends AbstractComponent {
action.equals(SearchTransportService.CLEAR_SCROLL_CONTEXTS_ACTION_NAME);
}
static boolean checkSameUserPermissions(String action, TransportRequest request, Authentication authentication) {
final boolean actionAllowed = SAME_USER_PRIVILEGE.test(action);
if (actionAllowed) {
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();
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];
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;
}
private 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.isRunAs();
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);
}
private ElasticsearchSecurityException denial(Authentication authentication, String action, TransportRequest request) {
auditTrail.accessDenied(authentication.getUser(), action, request);
return denialException(authentication, action);

View File

@ -10,18 +10,15 @@ import org.elasticsearch.common.util.concurrent.CountDown;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.support.AutomatonPredicate;
import org.elasticsearch.xpack.security.support.Automatons;
import org.elasticsearch.xpack.security.user.SystemUser;
import java.util.Collection;
import java.util.Collections;
import java.util.function.BiConsumer;
import java.util.function.Predicate;
public final class AuthorizationUtils {
private static final Predicate<String> INTERNAL_PREDICATE = new AutomatonPredicate(Automatons.patterns("internal:*"));
private static final Predicate<String> INTERNAL_PREDICATE = Automatons.predicate("internal:*");
private AuthorizationUtils() {}
@ -72,14 +69,13 @@ public final class AuthorizationUtils {
public static class AsyncAuthorizer {
private final ActionListener listener;
private final BiConsumer<Collection<Role>, Collection<Role>> consumer;
private final BiConsumer<Role, Role> consumer;
private final Authentication authentication;
private volatile Collection<Role> userRoles;
private volatile Collection<Role> runAsRoles;
private volatile Role userRoles;
private volatile Role runAsRoles;
private CountDown countDown = new CountDown(2); // we expect only two responses!!
public AsyncAuthorizer(Authentication authentication, ActionListener listener, BiConsumer<Collection<Role>,
Collection<Role>> consumer) {
public AsyncAuthorizer(Authentication authentication, ActionListener listener, BiConsumer<Role, Role> consumer) {
this.consumer = consumer;
this.listener = listener;
this.authentication = authentication;
@ -87,25 +83,25 @@ public final class AuthorizationUtils {
public void authorize(AuthorizationService service) {
if (SystemUser.is(authentication.getUser())) {
setUserRoles(Collections.emptyList()); // we can inform the listener immediately - nothing to fetch for us on system user
setRunAsRoles(Collections.emptyList());
setUserRoles(null); // we can inform the listener immediately - nothing to fetch for us on system user
setRunAsRoles(null);
} else {
service.roles(authentication.getUser(), ActionListener.wrap(this::setUserRoles, listener::onFailure));
if (authentication.isRunAs()) {
assert authentication.getRunAsUser() != null : "runAs user is null but shouldn't";
service.roles(authentication.getRunAsUser(), ActionListener.wrap(this::setRunAsRoles, listener::onFailure));
} else {
setRunAsRoles(Collections.emptyList());
setRunAsRoles(null);
}
}
}
private void setUserRoles(Collection<Role> roles) {
private void setUserRoles(Role roles) {
this.userRoles = roles;
maybeRun();
}
private void setRunAsRoles(Collection<Role> roles) {
private void setRunAsRoles(Role roles) {
this.runAsRoles = roles;
maybeRun();
}

View File

@ -9,13 +9,12 @@ import org.elasticsearch.cluster.metadata.AliasOrIndex;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.xpack.security.SecurityTemplateService;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.permission.SuperuserRole;
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.user.XPackUser;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@ -29,10 +28,10 @@ class AuthorizedIndices {
private final User user;
private final String action;
private final MetaData metaData;
private final Collection<Role> userRoles;
private final Role userRoles;
private List<String> authorizedIndices;
AuthorizedIndices(User user, Collection<Role> userRoles, String action, MetaData metaData) {
AuthorizedIndices(User user, Role userRoles, String action, MetaData metaData) {
this.user = user;
this.userRoles = userRoles;
this.action = action;
@ -47,16 +46,7 @@ class AuthorizedIndices {
}
private List<String> load() {
if (userRoles.isEmpty()) {
return Collections.emptyList();
}
List<Predicate<String>> predicates = new ArrayList<>();
for (Role userRole : userRoles) {
predicates.add(userRole.indices().allowedIndicesMatcher(action));
}
Predicate<String> predicate = predicates.stream().reduce(s -> false, Predicate::or);
Predicate<String> predicate = userRoles.indices().allowedIndicesMatcher(action);
List<String> indicesAndAliases = new ArrayList<>();
// TODO: can this be done smarter? I think there are usually more indices/aliases in the cluster then indices defined a roles?
@ -67,7 +57,7 @@ class AuthorizedIndices {
}
}
if (XPackUser.is(user) == false && Arrays.binarySearch(user.roles(), SuperuserRole.NAME) < 0) {
if (XPackUser.is(user) == false && Arrays.binarySearch(user.roles(), ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR.getName()) < 0) {
// we should filter out the .security index from wildcards
indicesAndAliases.remove(SecurityTemplateService.SECURITY_INDEX_NAME);
}

View File

@ -23,7 +23,6 @@ import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.json.JsonXContent;
import org.elasticsearch.xpack.common.xcontent.XContentUtils;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
import org.elasticsearch.xpack.security.support.MetadataUtils;
import org.elasticsearch.xpack.security.support.Validation;
@ -241,7 +240,7 @@ public class RoleDescriptor implements ToXContent {
}
String currentFieldName = null;
String[] names = null;
String query = null;
BytesReference query = null;
String[] privileges = null;
String[] grantedFields = null;
String[] deniedFields = null;
@ -265,15 +264,16 @@ public class RoleDescriptor implements ToXContent {
if (token == XContentParser.Token.START_OBJECT) {
XContentBuilder builder = JsonXContent.contentBuilder();
XContentHelper.copyCurrentStructure(builder.generator(), parser);
query = builder.string();
} else if (token == XContentParser.Token.VALUE_STRING){
query = builder.bytes();
} else if (token == XContentParser.Token.VALUE_STRING) {
final String text = parser.text();
if (text.isEmpty() == false) {
query = text;
query = new BytesArray(text);
}
} else if (token != XContentParser.Token.VALUE_NULL) {
throw new ElasticsearchParseException("failed to parse indices privileges for role [{}]. expected field [{}] " +
"value to be null, a string, or an object, but found [{}] instead", roleName, currentFieldName, token);
"value to be null, a string, an array, or an object, but found [{}] instead", roleName, currentFieldName,
token);
}
} else if (ParseFieldMatcher.STRICT.match(currentFieldName, Fields.FIELD_PERMISSIONS)) {
if (token == XContentParser.Token.START_OBJECT) {
@ -347,7 +347,8 @@ public class RoleDescriptor implements ToXContent {
return RoleDescriptor.IndicesPrivileges.builder()
.indices(names)
.privileges(privileges)
.fieldPermissions(new FieldPermissions(grantedFields, deniedFields))
.grantedFields(grantedFields)
.deniedFields(deniedFields)
.query(query)
.build();
}
@ -362,7 +363,8 @@ public class RoleDescriptor implements ToXContent {
private String[] indices;
private String[] privileges;
private FieldPermissions fieldPermissions = new FieldPermissions();
private String[] grantedFields = null;
private String[] deniedFields = null;
private BytesReference query;
private IndicesPrivileges() {
@ -380,8 +382,14 @@ public class RoleDescriptor implements ToXContent {
return this.privileges;
}
public FieldPermissions getFieldPermissions() {
return fieldPermissions;
@Nullable
public String[] getGrantedFields() {
return this.grantedFields;
}
@Nullable
public String[] getDeniedFields() {
return this.deniedFields;
}
@Nullable
@ -395,9 +403,27 @@ public class RoleDescriptor implements ToXContent {
sb.append("indices=[").append(Strings.arrayToCommaDelimitedString(indices));
sb.append("], privileges=[").append(Strings.arrayToCommaDelimitedString(privileges));
sb.append("], ");
sb.append(fieldPermissions.toString());
if (grantedFields != null || deniedFields != null) {
sb.append(RoleDescriptor.Fields.FIELD_PERMISSIONS).append("=[");
if (grantedFields == null) {
sb.append(RoleDescriptor.Fields.GRANT_FIELDS).append("=null");
} else {
sb.append(RoleDescriptor.Fields.GRANT_FIELDS).append("=[")
.append(Strings.arrayToCommaDelimitedString(grantedFields));
sb.append("]");
}
if (deniedFields == null) {
sb.append(", ").append(RoleDescriptor.Fields.EXCEPT_FIELDS).append("=null");
} else {
sb.append(", ").append(RoleDescriptor.Fields.EXCEPT_FIELDS).append("=[")
.append(Strings.arrayToCommaDelimitedString(deniedFields));
sb.append("]");
}
sb.append("]");
}
if (query != null) {
sb.append(", query=").append(query.utf8ToString());
sb.append(", query=");
sb.append(query.utf8ToString());
}
sb.append("]");
return sb.toString();
@ -412,7 +438,8 @@ public class RoleDescriptor implements ToXContent {
if (!Arrays.equals(indices, that.indices)) return false;
if (!Arrays.equals(privileges, that.privileges)) return false;
if (fieldPermissions.equals(that.fieldPermissions) == false) return false;
if (!Arrays.equals(grantedFields, that.grantedFields)) return false;
if (!Arrays.equals(deniedFields, that.deniedFields)) return false;
return !(query != null ? !query.equals(that.query) : that.query != null);
}
@ -420,7 +447,8 @@ public class RoleDescriptor implements ToXContent {
public int hashCode() {
int result = Arrays.hashCode(indices);
result = 31 * result + Arrays.hashCode(privileges);
result = 31 * result + fieldPermissions.hashCode();
result = 31 * result + Arrays.hashCode(grantedFields);
result = 31 * result + Arrays.hashCode(deniedFields);
result = 31 * result + (query != null ? query.hashCode() : 0);
return result;
}
@ -430,7 +458,16 @@ public class RoleDescriptor implements ToXContent {
builder.startObject();
builder.array("names", indices);
builder.array("privileges", privileges);
builder = fieldPermissions.toXContent(builder, params);
if (grantedFields != null || deniedFields != null) {
builder.startObject(RoleDescriptor.Fields.FIELD_PERMISSIONS.getPreferredName());
if (grantedFields != null) {
builder.array(RoleDescriptor.Fields.GRANT_FIELDS.getPreferredName(), grantedFields);
}
if (deniedFields != null) {
builder.array(RoleDescriptor.Fields.EXCEPT_FIELDS.getPreferredName(), deniedFields);
}
builder.endObject();
}
if (query != null) {
builder.field("query", query.utf8ToString());
}
@ -446,7 +483,8 @@ public class RoleDescriptor implements ToXContent {
@Override
public void readFrom(StreamInput in) throws IOException {
this.indices = in.readStringArray();
this.fieldPermissions = new FieldPermissions(in);
this.grantedFields = in.readOptionalStringArray();
this.deniedFields = in.readOptionalStringArray();
this.privileges = in.readStringArray();
this.query = in.readOptionalBytesReference();
}
@ -454,7 +492,8 @@ public class RoleDescriptor implements ToXContent {
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeStringArray(indices);
fieldPermissions.writeTo(out);
out.writeOptionalStringArray(grantedFields);
out.writeOptionalStringArray(deniedFields);
out.writeStringArray(privileges);
out.writeOptionalBytesReference(query);
}
@ -476,8 +515,13 @@ public class RoleDescriptor implements ToXContent {
return this;
}
public Builder fieldPermissions(FieldPermissions fieldPermissions) {
indicesPrivileges.fieldPermissions = fieldPermissions;
public Builder grantedFields(String... grantedFields) {
indicesPrivileges.grantedFields = grantedFields;
return this;
}
public Builder deniedFields(String... deniedFields) {
indicesPrivileges.deniedFields = deniedFields;
return this;
}
@ -486,7 +530,11 @@ public class RoleDescriptor implements ToXContent {
}
public Builder query(@Nullable BytesReference query) {
indicesPrivileges.query = query;
if (query == null) {
indicesPrivileges.query = null;
} else {
indicesPrivileges.query = query;
}
return this;
}

View File

@ -88,36 +88,5 @@ public class IndicesAccessControl {
public Set<BytesReference> getQueries() {
return queries;
}
public IndexAccessControl merge(IndexAccessControl other) {
if (other.isGranted() == false) {
// nothing to merge
return this;
}
final boolean granted = this.granted;
if (granted == false) {
// we do not support negatives, so if the current isn't granted - just return other
assert other.isGranted();
return other;
}
FieldPermissions newPermissions = FieldPermissions.merge(this.fieldPermissions, other.fieldPermissions);
Set<BytesReference> queries = null;
if (this.queries != null && other.getQueries() != null) {
queries = new HashSet<>();
if (this.queries != null) {
queries.addAll(this.queries);
}
if (other.getQueries() != null) {
queries.addAll(other.getQueries());
}
queries = unmodifiableSet(queries);
}
return new IndexAccessControl(granted, newPermissions, queries);
}
}
}

View File

@ -5,93 +5,30 @@
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import java.util.List;
import java.util.Objects;
import java.util.function.Predicate;
/**
* A permission that is based on privileges for cluster wide actions
*/
public interface ClusterPermission extends Permission {
public final class ClusterPermission {
boolean check(String action, TransportRequest request, Authentication authentication);
public static final ClusterPermission NONE = new ClusterPermission(ClusterPrivilege.NONE);
class Core implements ClusterPermission {
private final ClusterPrivilege privilege;
private final Predicate<String> predicate;
public static final Core NONE = new Core(ClusterPrivilege.NONE) {
@Override
public boolean check(String action, TransportRequest request, Authentication authentication) {
return false;
}
@Override
public boolean isEmpty() {
return true;
}
};
private final ClusterPrivilege privilege;
private final Predicate<String> predicate;
Core(ClusterPrivilege privilege) {
this.privilege = privilege;
this.predicate = privilege.predicate();
}
public ClusterPrivilege privilege() {
return privilege;
}
@Override
public boolean check(String action, TransportRequest request, Authentication authentication) {
return predicate.test(action);
}
@Override
public boolean isEmpty() {
return false;
}
ClusterPermission(ClusterPrivilege privilege) {
this.privilege = privilege;
this.predicate = privilege.predicate();
}
class Globals implements ClusterPermission {
private final List<GlobalPermission> globals;
Globals(List<GlobalPermission> globals) {
this.globals = globals;
}
@Override
public boolean check(String action, TransportRequest request, Authentication authentication) {
if (globals == null) {
return false;
}
for (GlobalPermission global : globals) {
Objects.requireNonNull(global, "global must not be null");
Objects.requireNonNull(global.indices(), "global.indices() must not be null");
if (global.cluster().check(action, request, authentication)) {
return true;
}
}
return false;
}
@Override
public boolean isEmpty() {
if (globals == null || globals.isEmpty()) {
return true;
}
for (GlobalPermission global : globals) {
if (!global.isEmpty()) {
return false;
}
}
return true;
}
public ClusterPrivilege privilege() {
return privilege;
}
public boolean check(String action) {
return predicate.test(action);
}
}

View File

@ -1,88 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.esnative.NativeRealm;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.action.user.AuthenticateAction;
import org.elasticsearch.xpack.security.action.user.ChangePasswordAction;
import org.elasticsearch.xpack.security.action.user.UserRequest;
import org.elasticsearch.xpack.security.authz.permission.RunAsPermission.Core;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.Privilege.Name;
import org.elasticsearch.transport.TransportRequest;
/**
* A default role that will be applied to all users other than the internal {@link org.elasticsearch.xpack.security.user.SystemUser}. This
* role grants access to actions that every user should be able to execute such as the ability to change their password and execute the
* authenticate endpoint to get information about themselves
*/
public class DefaultRole extends Role {
private static final ClusterPermission.Core CLUSTER_PERMISSION =
new SameUserClusterPermission(ClusterPrivilege.get(new Name(ChangePasswordAction.NAME, AuthenticateAction.NAME)));
private static final IndicesPermission.Core INDICES_PERMISSION = IndicesPermission.Core.NONE;
private static final RunAsPermission.Core RUN_AS_PERMISSION = Core.NONE;
public static final String NAME = "__default_role";
public static final DefaultRole INSTANCE = new DefaultRole();
private DefaultRole() {
super(NAME, CLUSTER_PERMISSION, INDICES_PERMISSION, RUN_AS_PERMISSION);
}
private static class SameUserClusterPermission extends ClusterPermission.Core {
private SameUserClusterPermission(ClusterPrivilege privilege) {
super(privilege);
}
@Override
public boolean check(String action, TransportRequest request, Authentication authentication) {
final boolean actionAllowed = super.check(action, request, authentication);
if (actionAllowed) {
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();
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];
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.isRunAs();
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);
}
}

View File

@ -13,8 +13,6 @@ import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.regex.Regex;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.index.mapper.AllFieldMapper;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
@ -26,14 +24,10 @@ import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import static org.apache.lucene.util.automaton.MinimizationOperations.minimize;
import static org.apache.lucene.util.automaton.Operations.DEFAULT_MAX_DETERMINIZED_STATES;
import static org.apache.lucene.util.automaton.Operations.isTotal;
import static org.apache.lucene.util.automaton.Operations.run;
import static org.apache.lucene.util.automaton.Operations.sameLanguage;
import static org.apache.lucene.util.automaton.Operations.subsetOf;
import static org.apache.lucene.util.automaton.Operations.union;
import static org.elasticsearch.xpack.security.support.Automatons.minusAndDeterminize;
import static org.elasticsearch.xpack.security.support.Automatons.minusAndMinimize;
/**
* Stores patterns to fields which access is granted or denied to and maintains an automaton that can be used to check if permission is
@ -43,30 +37,42 @@ import static org.elasticsearch.xpack.security.support.Automatons.minusAndDeterm
* 1. It has to match the patterns in grantedFieldsArray
* 2. it must not match the patterns in deniedFieldsArray
*/
public class FieldPermissions implements Writeable, ToXContent {
public final class FieldPermissions implements Writeable {
public static final FieldPermissions DEFAULT = new FieldPermissions();
// the patterns for fields which we allow access to. if gratedFieldsArray is null we assume that all fields are grated access to
String[] grantedFieldsArray;
private final String[] grantedFieldsArray;
// the patterns for fields which we deny access to. if this is an empty list or null we assume that we do not deny access to any
// field explicitly
String[] deniedFieldsArray;
private final String[] deniedFieldsArray;
// an automaton that matches all strings that match the patterns in permittedFieldsArray but does not match those that also match a
// pattern in deniedFieldsArray. If permittedFieldsAutomaton is null we assume that all fields are granted access to.
Automaton permittedFieldsAutomaton;
private final Automaton permittedFieldsAutomaton;
// we cannot easily determine if all fields are allowed and we can therefore also allow access to the _all field hence we deny access
// to _all unless this was explicitly configured.
boolean allFieldIsAllowed = false;
private final boolean allFieldIsAllowed;
public FieldPermissions() {
this(null, null);
}
public FieldPermissions(StreamInput in) throws IOException {
this(in.readOptionalStringArray(), in.readOptionalStringArray());
}
public FieldPermissions(@Nullable String[] grantedFieldsArray, @Nullable String[] deniedFieldsArray) {
this(grantedFieldsArray, deniedFieldsArray, initializePermittedFieldsAutomaton(grantedFieldsArray, deniedFieldsArray),
checkAllFieldIsAllowed(grantedFieldsArray, deniedFieldsArray));
}
FieldPermissions(@Nullable String[] grantedFieldsArray, @Nullable String[] deniedFieldsArray,
Automaton permittedFieldsAutomaton, boolean allFieldIsAllowed) {
this.grantedFieldsArray = grantedFieldsArray;
this.deniedFieldsArray = deniedFieldsArray;
permittedFieldsAutomaton = initializePermittedFieldsAutomaton(grantedFieldsArray, deniedFieldsArray);
allFieldIsAllowed = checkAllFieldIsAllowed(grantedFieldsArray, deniedFieldsArray);
this.permittedFieldsAutomaton = permittedFieldsAutomaton;
this.allFieldIsAllowed = allFieldIsAllowed;
}
private static boolean checkAllFieldIsAllowed(String[] grantedFieldsArray, String[] deniedFieldsArray) {
@ -87,8 +93,7 @@ public class FieldPermissions implements Writeable, ToXContent {
return false;
}
private static Automaton initializePermittedFieldsAutomaton(final String[] grantedFieldsArray,
final String[] deniedFieldsArray) {
private static Automaton initializePermittedFieldsAutomaton(final String[] grantedFieldsArray, final String[] deniedFieldsArray) {
Automaton grantedFieldsAutomaton;
if (grantedFieldsArray == null || containsWildcard(grantedFieldsArray)) {
grantedFieldsAutomaton = Automatons.MATCH_ALL;
@ -107,7 +112,7 @@ public class FieldPermissions implements Writeable, ToXContent {
Arrays.toString(grantedFieldsArray));
}
grantedFieldsAutomaton = minusAndDeterminize(grantedFieldsAutomaton, deniedFieldsAutomaton);
grantedFieldsAutomaton = minusAndMinimize(grantedFieldsAutomaton, deniedFieldsAutomaton);
return grantedFieldsAutomaton;
}
@ -120,26 +125,12 @@ public class FieldPermissions implements Writeable, ToXContent {
return false;
}
public FieldPermissions() {
this(null, null);
}
@Override
public void writeTo(StreamOutput out) throws IOException {
out.writeOptionalStringArray(grantedFieldsArray);
out.writeOptionalStringArray(deniedFieldsArray);
}
@Nullable
String[] getGrantedFieldsArray() {
return grantedFieldsArray;
}
@Nullable
String[] getDeniedFieldsArray() {
return deniedFieldsArray;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@ -164,21 +155,6 @@ public class FieldPermissions implements Writeable, ToXContent {
return sb.toString();
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
if (grantedFieldsArray != null || deniedFieldsArray != null) {
builder.startObject(RoleDescriptor.Fields.FIELD_PERMISSIONS.getPreferredName());
if (grantedFieldsArray != null) {
builder.array(RoleDescriptor.Fields.GRANT_FIELDS.getPreferredName(), grantedFieldsArray);
}
if (deniedFieldsArray != null) {
builder.array(RoleDescriptor.Fields.EXCEPT_FIELDS.getPreferredName(), deniedFieldsArray);
}
builder.endObject();
}
return builder;
}
/**
* Returns true if this field permission policy allows access to the field and false if not.
* fieldName can be a wildcard.
@ -187,22 +163,28 @@ public class FieldPermissions implements Writeable, ToXContent {
return isTotal(permittedFieldsAutomaton) || run(permittedFieldsAutomaton, fieldName);
}
// Also, if one grants no access to fields and the other grants all access, merging should result in all access...
public static FieldPermissions merge(FieldPermissions p1, FieldPermissions p2) {
Automaton mergedPermittedFieldsAutomaton;
// we only allow the union of the two automatons
mergedPermittedFieldsAutomaton = union(p1.permittedFieldsAutomaton, p2.permittedFieldsAutomaton);
// need to minimize otherwise isTotal() might return false even if one of the merged ones returned true before
mergedPermittedFieldsAutomaton = minimize(mergedPermittedFieldsAutomaton, DEFAULT_MAX_DETERMINIZED_STATES);
// if one of them allows access to _all we allow it for the merged too
boolean allFieldIsAllowedInMerged = p1.allFieldIsAllowed || p2.allFieldIsAllowed;
return new MergedFieldPermissions(mergedPermittedFieldsAutomaton, allFieldIsAllowedInMerged);
Automaton getPermittedFieldsAutomaton() {
return permittedFieldsAutomaton;
}
@Nullable
String[] getGrantedFieldsArray() {
return grantedFieldsArray;
}
@Nullable
String[] getDeniedFieldsArray() {
return deniedFieldsArray;
}
public boolean hasFieldLevelSecurity() {
return isTotal(permittedFieldsAutomaton) == false;
}
boolean isAllFieldIsAllowed() {
return allFieldIsAllowed;
}
public Set<String> resolveAllowedFields(Set<String> allowedMetaFields, MapperService mapperService) {
HashSet<String> finalAllowedFields = new HashSet<>();
// we always add the allowed meta fields because we must make sure access is not denied accidentally
@ -232,59 +214,14 @@ public class FieldPermissions implements Writeable, ToXContent {
// Probably incorrect - comparing Object[] arrays with Arrays.equals
if (!Arrays.equals(grantedFieldsArray, that.grantedFieldsArray)) return false;
// Probably incorrect - comparing Object[] arrays with Arrays.equals
if (!Arrays.equals(deniedFieldsArray, that.deniedFieldsArray)) return false;
return sameLanguage(permittedFieldsAutomaton, that.permittedFieldsAutomaton);
return Arrays.equals(deniedFieldsArray, that.deniedFieldsArray);
}
@Override
public int hashCode() {
int result = Arrays.hashCode(grantedFieldsArray);
result = 31 * result + Arrays.hashCode(deniedFieldsArray);
result = 31 * result + permittedFieldsAutomaton.hashCode();
result = 31 * result + (allFieldIsAllowed ? 1 : 0);
return result;
}
/**
* When we merge field permissions we need to union all the allowed fields. We do this by a union of the automatons
* that define which fields are granted access too. However, that means that after merging we cannot know anymore
* which strings defined the automatons. Hence we make a new class that only has an automaton for the fields that
* we grant access to and that throws an exception whenever we try to access the original patterns that lead to
* the automaton.
*/
public static class MergedFieldPermissions extends FieldPermissions {
public MergedFieldPermissions(Automaton grantedFields, boolean allFieldIsAllowed) {
assert grantedFields != null;
this.permittedFieldsAutomaton = grantedFields;
this.grantedFieldsArray = null;
this.deniedFieldsArray = null;
this.allFieldIsAllowed = allFieldIsAllowed;
}
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
throw new UnsupportedOperationException("Cannot build xcontent for merged field permissions");
}
@Override
public String toString() {
throw new UnsupportedOperationException("Cannot build string for merged field permissions");
}
@Override
public void writeTo(StreamOutput out) throws IOException {
throw new UnsupportedOperationException("Cannot stream for merged field permissions");
}
@Nullable
public String[] getGrantedFieldsArray() {
throw new UnsupportedOperationException("Merged field permissions does not maintain sets");
}
@Nullable
public String[] getDeniedFieldsArray() {
throw new UnsupportedOperationException("Merged field permissions does not maintain sets");
}
}
}

View File

@ -0,0 +1,181 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.apache.lucene.util.automaton.Operations;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.cache.Cache;
import org.elasticsearch.common.cache.CacheBuilder;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.set.Sets;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import static org.elasticsearch.xpack.security.Security.setting;
/**
* A service for managing the caching of {@link FieldPermissions} as these may often need to be combined or created and internally they
* use an {@link org.apache.lucene.util.automaton.Automaton}, which can be costly to create once you account for minimization
*/
public final class FieldPermissionsCache {
public static final Setting<Long> CACHE_SIZE_SETTING = Setting.longSetting(
setting("authz.store.roles.field_permissions.cache.max_size_in_bytes"), 100 * 1024 * 1024, -1L, Property.NodeScope);
private final Cache<Key, FieldPermissions> cache;
public FieldPermissionsCache(Settings settings) {
this.cache = CacheBuilder.<Key, FieldPermissions>builder()
.setMaximumWeight(CACHE_SIZE_SETTING.get(settings))
// this is not completely accurate but in most cases the automaton should be the most expensive aspect
.weigher((key, fieldPermissions) -> fieldPermissions.getPermittedFieldsAutomaton().ramBytesUsed())
.build();
}
/**
* Gets a {@link FieldPermissions} instance that corresponds to the granted and denied parameters. The instance may come from the cache
* or if it gets created, the instance will be cached
*/
FieldPermissions getFieldPermissions(String[] granted, String[] denied) {
final Set<String> grantedSet;
if (granted != null) {
grantedSet = new HashSet<>(granted.length);
Collections.addAll(grantedSet, granted);
} else {
grantedSet = null;
}
final Set<String> deniedSet;
if (denied != null) {
deniedSet = new HashSet<>(denied.length);
Collections.addAll(deniedSet, denied);
} else {
deniedSet = null;
}
return getFieldPermissions(grantedSet, deniedSet);
}
/**
* Gets a {@link FieldPermissions} instance that corresponds to the granted and denied parameters. The instance may come from the cache
* or if it gets created, the instance will be cached
*/
public FieldPermissions getFieldPermissions(Set<String> granted, Set<String> denied) {
Key fpKey = new Key(granted == null ? null : Collections.unmodifiableSet(granted),
denied == null ? null : Collections.unmodifiableSet(denied));
try {
return cache.computeIfAbsent(fpKey,
(key) -> new FieldPermissions(key.grantedFields == null ? null : key.grantedFields.toArray(Strings.EMPTY_ARRAY),
key.deniedFields == null ? null : key.deniedFields.toArray(Strings.EMPTY_ARRAY)));
} catch (ExecutionException e) {
throw new ElasticsearchException("unable to compute field permissions", e);
}
}
/**
* Returns a field permissions object that corresponds to the merging of the given field permissions and caches the instance if one was
* not found in the cache.
*/
FieldPermissions getFieldPermissions(Collection<FieldPermissions> fieldPermissionsCollection) {
Optional<FieldPermissions> allowAllFieldPermissions = fieldPermissionsCollection.stream()
.filter((fp) -> Operations.isTotal(fp.getPermittedFieldsAutomaton()))
.findFirst();
return allowAllFieldPermissions.orElseGet(() -> {
final Set<String> allowedFields;
Optional<FieldPermissions> nullAllowedFields = fieldPermissionsCollection.stream()
.filter((fieldPermissions) -> fieldPermissions.getGrantedFieldsArray() == null)
.findFirst();
if (nullAllowedFields.isPresent()) {
allowedFields = null;
} else {
allowedFields = fieldPermissionsCollection.stream()
.flatMap(fieldPermissions -> Arrays.stream(fieldPermissions.getGrantedFieldsArray()))
.collect(Collectors.toSet());
}
final Set<String> deniedFields = fieldPermissionsCollection.stream()
.filter(fieldPermissions -> fieldPermissions.getDeniedFieldsArray() != null)
.flatMap(fieldPermissions -> Arrays.stream(fieldPermissions.getDeniedFieldsArray()))
.collect(Collectors.toSet());
try {
return cache.computeIfAbsent(new Key(allowedFields, deniedFields),
(key) -> {
final String[] actualDeniedFields = key.deniedFields == null ? null :
computeDeniedFieldsForPermissions(fieldPermissionsCollection, key.deniedFields);
return new FieldPermissions(key.grantedFields == null ? null : key.grantedFields.toArray(Strings.EMPTY_ARRAY),
actualDeniedFields);
});
} catch (ExecutionException e) {
throw new ElasticsearchException("unable to compute field permissions", e);
}
});
}
private static String[] computeDeniedFieldsForPermissions(Collection<FieldPermissions> fieldPermissionsCollection,
Set<String> allDeniedFields) {
Set<String> allowedDeniedFields = new HashSet<>();
fieldPermissionsCollection
.stream()
.filter(fieldPermissions -> fieldPermissions.getDeniedFieldsArray() != null)
.forEach((fieldPermissions) -> {
String[] deniedFieldsForPermission = fieldPermissions.getDeniedFieldsArray();
fieldPermissionsCollection.forEach((fp) -> {
if (fp != fieldPermissions) {
Arrays.stream(deniedFieldsForPermission).forEach((field) -> {
if (fp.grantsAccessTo(field)) {
allowedDeniedFields.add(field);
}
});
}
});
});
Set<String> difference = Sets.difference(allDeniedFields, allowedDeniedFields);
if (difference.isEmpty()) {
return null;
} else {
return difference.toArray(Strings.EMPTY_ARRAY);
}
}
private static class Key {
private final Set<String> grantedFields;
private final Set<String> deniedFields;
Key(Set<String> grantedFields, Set<String> deniedFields) {
this.grantedFields = grantedFields == null ? null : Collections.unmodifiableSet(grantedFields);
this.deniedFields = deniedFields == null ? null : Collections.unmodifiableSet(deniedFields);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Key)) return false;
Key key = (Key) o;
if (grantedFields != null ? !grantedFields.equals(key.grantedFields) : key.grantedFields != null) return false;
return deniedFields != null ? deniedFields.equals(key.deniedFields) : key.deniedFields == null;
}
@Override
public int hashCode() {
int result = grantedFields != null ? grantedFields.hashCode() : 0;
result = 31 * result + (deniedFields != null ? deniedFields.hashCode() : 0);
return result;
}
}
}

View File

@ -1,100 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* A composite permission that combines {@code cluster}, {@code indices} and {@code run_as} permissions
*/
public class GlobalPermission implements Permission {
public static final GlobalPermission NONE = new GlobalPermission(ClusterPermission.Core.NONE, IndicesPermission.Core.NONE,
RunAsPermission.Core.NONE);
private final ClusterPermission cluster;
private final IndicesPermission indices;
private final RunAsPermission runAs;
GlobalPermission(ClusterPermission cluster, IndicesPermission indices, RunAsPermission runAs) {
this.cluster = cluster;
this.indices = indices;
this.runAs = runAs;
}
public ClusterPermission cluster() {
return cluster;
}
public IndicesPermission indices() {
return indices;
}
public RunAsPermission runAs() {
return runAs;
}
@Override
public boolean isEmpty() {
return (cluster == null || cluster.isEmpty()) && (indices == null || indices.isEmpty()) && (runAs == null || runAs.isEmpty());
}
/**
* Returns whether at least one group encapsulated by this indices permissions is authorized to execute the
* 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) {
Map<String, IndicesAccessControl.IndexAccessControl> indexPermissions = indices.authorize(
action, requestedIndicesOrAliases, metaData
);
// At least one role / indices permission set need to match with all the requested indices/aliases:
boolean granted = true;
for (Map.Entry<String, IndicesAccessControl.IndexAccessControl> entry : indexPermissions.entrySet()) {
if (!entry.getValue().isGranted()) {
granted = false;
break;
}
}
return new IndicesAccessControl(granted, indexPermissions);
}
public static class Compound extends GlobalPermission {
Compound(List<GlobalPermission> globals) {
super(new ClusterPermission.Globals(globals), new IndicesPermission.Globals(globals), new RunAsPermission.Globals(globals));
}
public static Compound.Builder builder() {
return new Compound.Builder();
}
public static class Builder {
private List<GlobalPermission> globals = new ArrayList<>();
private Builder() {
}
public Compound.Builder add(GlobalPermission global) {
globals.add(global);
return this;
}
public Compound build() {
return new Compound(Collections.unmodifiableList(globals));
}
}
}
}

View File

@ -12,12 +12,10 @@ import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.security.support.AutomatonPredicate;
import org.elasticsearch.xpack.security.support.Automatons;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@ -30,7 +28,6 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.function.Predicate;
import static java.util.Collections.emptyMap;
import static java.util.Collections.unmodifiableMap;
import static java.util.Collections.unmodifiableSet;
@ -38,12 +35,45 @@ import static java.util.Collections.unmodifiableSet;
* A permission that is based on privileges for index related actions executed
* on specific indices
*/
public interface IndicesPermission extends Permission, Iterable<IndicesPermission.Group> {
public final class IndicesPermission implements Iterable<IndicesPermission.Group> {
public static final IndicesPermission NONE = new IndicesPermission();
private final Function<String, Predicate<String>> loadingFunction;
private final ConcurrentHashMap<String, Predicate<String>> allowedIndicesMatchersForAction = new ConcurrentHashMap<>();
private final Group[] groups;
public IndicesPermission(Group... groups) {
this.groups = groups;
loadingFunction = (action) -> {
List<String> indices = new ArrayList<>();
for (Group group : groups) {
if (group.actionMatcher.test(action)) {
indices.addAll(Arrays.asList(group.indices));
}
}
return Automatons.predicate(indices);
};
}
@Override
public Iterator<Group> iterator() {
return Arrays.asList(groups).iterator();
}
public Group[] groups() {
return groups;
}
/**
* Authorizes the provided action against the provided indices, given the current cluster metadata
* @return A predicate that will match all the indices that this permission
* has the privilege for executing the given action on.
*/
Map<String, IndicesAccessControl.IndexAccessControl> authorize(String action, Set<String> requestedIndicesOrAliases, MetaData metaData);
public Predicate<String> allowedIndicesMatcher(String action) {
return allowedIndicesMatchersForAction.computeIfAbsent(action, loadingFunction);
}
/**
* Checks if the permission matches the provided action, without looking at indices.
@ -51,277 +81,83 @@ public interface IndicesPermission extends Permission, Iterable<IndicesPermissio
* The usecase for this is composite actions that are initially only authorized based on the action name (indices are not
* checked on the coordinating node), and properly authorized later at the shard level checking their indices as well.
*/
boolean check(String action);
class Core implements IndicesPermission {
public static final Core NONE = new Core() {
@Override
public Iterator<Group> iterator() {
return Collections.emptyIterator();
}
@Override
public boolean isEmpty() {
public boolean check(String action) {
for (Group group : groups) {
if (group.check(action)) {
return true;
}
};
private final Function<String, Predicate<String>> loadingFunction;
private final ConcurrentHashMap<String, Predicate<String>> allowedIndicesMatchersForAction = new ConcurrentHashMap<>();
private final Group[] groups;
public Core(List<Group> groups) {
this(groups.toArray(new Group[groups.size()]));
}
return false;
}
public Core(Group... groups) {
this.groups = groups;
loadingFunction = (action) -> {
List<String> indices = new ArrayList<>();
for (Group group : groups) {
if (group.actionMatcher.test(action)) {
indices.addAll(Arrays.asList(group.indices));
}
/**
* 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) {
// 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, Set<BytesReference>> 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);
if (aliasOrIndex != null) {
for (IndexMetaData indexMetaData : aliasOrIndex.getIndices()) {
concreteIndices.add(indexMetaData.getIndex().getName());
}
return new AutomatonPredicate(Automatons.patterns(Collections.unmodifiableList(indices)));
};
}
}
@Override
public Iterator<Group> iterator() {
return Arrays.asList(groups).iterator();
}
public Group[] groups() {
return groups;
}
@Override
public boolean isEmpty() {
return groups == null || groups.length == 0;
}
/**
* @return A predicate that will match all the indices that this permission
* has the privilege for executing the given action on.
*/
public Predicate<String> allowedIndicesMatcher(String action) {
return allowedIndicesMatchersForAction.computeIfAbsent(action, loadingFunction);
}
@Override
public boolean check(String action) {
for (Group group : groups) {
if (group.check(action)) {
return true;
}
}
return false;
}
@Override
public Map<String, IndicesAccessControl.IndexAccessControl> authorize(String action, Set<String> requestedIndicesOrAliases,
MetaData metaData) {
// 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, Set<BytesReference>> 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);
if (aliasOrIndex != null) {
for (IndexMetaData indexMetaData : aliasOrIndex.getIndices()) {
concreteIndices.add(indexMetaData.getIndex().getName());
}
}
for (Group group : groups) {
if (group.check(action, indexOrAlias)) {
granted = true;
for (String index : concreteIndices) {
if (fieldPermissionsByIndex.get(index) == null) {
fieldPermissionsByIndex.put(index, new HashSet<>());
}
fieldPermissionsByIndex.get(index).add(group.getFieldPermissions());
if (group.hasQuery()) {
Set<BytesReference> roleQueries = roleQueriesByIndex.get(index);
if (roleQueries == null) {
roleQueries = new HashSet<>();
roleQueriesByIndex.put(index, roleQueries);
}
roleQueries.add(group.getQuery());
}
}
}
}
if (concreteIndices.isEmpty()) {
grantedBuilder.put(indexOrAlias, granted);
} else {
for (String concreteIndex : concreteIndices) {
grantedBuilder.put(concreteIndex, granted);
}
}
}
Map<String, IndicesAccessControl.IndexAccessControl> indexPermissions = new HashMap<>();
for (Map.Entry<String, Boolean> entry : grantedBuilder.entrySet()) {
String index = entry.getKey();
Set<BytesReference> roleQueries = roleQueriesByIndex.get(index);
if (roleQueries != null) {
roleQueries = unmodifiableSet(roleQueries);
}
FieldPermissions fieldPermissions = new FieldPermissions();
Set<FieldPermissions> indexFieldPermissions = fieldPermissionsByIndex.get(index);
if (indexFieldPermissions != null) {
// get the first field permission entry because we do not want the merge to overwrite granted fields with null
fieldPermissions = indexFieldPermissions.iterator().next();
for (FieldPermissions fp : indexFieldPermissions) {
fieldPermissions = FieldPermissions.merge(fieldPermissions, fp);
}
}
indexPermissions.put(index, new IndicesAccessControl.IndexAccessControl(entry.getValue(), fieldPermissions, roleQueries));
}
return unmodifiableMap(indexPermissions);
}
}
class Globals implements IndicesPermission {
private final List<GlobalPermission> globals;
public Globals(List<GlobalPermission> globals) {
this.globals = globals;
}
@Override
public Iterator<Group> iterator() {
return globals == null || globals.isEmpty() ?
Collections.<Group>emptyIterator() :
new Globals.Iter(globals);
}
@Override
public boolean isEmpty() {
if (globals == null || globals.isEmpty()) {
return true;
}
for (GlobalPermission global : globals) {
if (!global.indices().isEmpty()) {
return false;
}
}
return true;
}
@Override
public boolean check(String action) {
if (globals == null) {
return false;
}
for (GlobalPermission global : globals) {
Objects.requireNonNull(global, "global must not be null");
Objects.requireNonNull(global.indices(), "global.indices() must not be null");
if (global.indices().check(action)) {
return true;
}
}
return false;
}
@Override
public Map<String, IndicesAccessControl.IndexAccessControl> authorize(String action, Set<String> requestedIndicesOrAliases,
MetaData metaData) {
if (isEmpty()) {
return emptyMap();
}
// What this code does is just merge `IndexAccessControl` instances from the permissions this class holds:
Map<String, IndicesAccessControl.IndexAccessControl> indicesAccessControl = null;
for (GlobalPermission permission : globals) {
Map<String, IndicesAccessControl.IndexAccessControl> temp = permission.indices().authorize(action,
requestedIndicesOrAliases, metaData);
if (indicesAccessControl == null) {
indicesAccessControl = new HashMap<>(temp);
} else {
for (Map.Entry<String, IndicesAccessControl.IndexAccessControl> entry : temp.entrySet()) {
IndicesAccessControl.IndexAccessControl existing = indicesAccessControl.get(entry.getKey());
if (existing != null) {
indicesAccessControl.put(entry.getKey(), existing.merge(entry.getValue()));
} else {
indicesAccessControl.put(entry.getKey(), entry.getValue());
if (group.check(action, indexOrAlias)) {
granted = true;
for (String index : concreteIndices) {
Set<FieldPermissions> fieldPermissions = fieldPermissionsByIndex.computeIfAbsent(index, (k) -> new HashSet<>());
fieldPermissions.add(group.getFieldPermissions());
if (group.hasQuery()) {
Set<BytesReference> roleQueries = roleQueriesByIndex.computeIfAbsent(index, (k) -> new HashSet<>());
roleQueries.addAll(group.getQuery());
}
}
}
}
if (indicesAccessControl == null) {
return emptyMap();
if (concreteIndices.isEmpty()) {
grantedBuilder.put(indexOrAlias, granted);
} else {
return unmodifiableMap(indicesAccessControl);
for (String concreteIndex : concreteIndices) {
grantedBuilder.put(concreteIndex, granted);
}
}
}
static class Iter implements Iterator<Group> {
private final Iterator<GlobalPermission> globals;
private Iterator<Group> current;
Iter(List<GlobalPermission> globals) {
this.globals = globals.iterator();
advance();
Map<String, IndicesAccessControl.IndexAccessControl> indexPermissions = new HashMap<>();
for (Map.Entry<String, Boolean> entry : grantedBuilder.entrySet()) {
String index = entry.getKey();
Set<BytesReference> roleQueries = roleQueriesByIndex.get(index);
if (roleQueries != null) {
roleQueries = unmodifiableSet(roleQueries);
}
@Override
public boolean hasNext() {
return current != null && current.hasNext();
}
@Override
public Group next() {
Group group = current.next();
advance();
return group;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
private void advance() {
if (current != null && current.hasNext()) {
return;
}
if (!globals.hasNext()) {
// we've reached the end of the globals array
current = null;
return;
}
while (globals.hasNext()) {
IndicesPermission indices = globals.next().indices();
if (!indices.isEmpty()) {
current = indices.iterator();
return;
}
}
current = null;
final FieldPermissions fieldPermissions;
final Set<FieldPermissions> indexFieldPermissions = fieldPermissionsByIndex.get(index);
if (indexFieldPermissions != null && indexFieldPermissions.isEmpty() == false) {
fieldPermissions = indexFieldPermissions.size() == 1 ? indexFieldPermissions.iterator().next() :
fieldPermissionsCache.getFieldPermissions(indexFieldPermissions);
} else {
fieldPermissions = FieldPermissions.DEFAULT;
}
indexPermissions.put(index, new IndicesAccessControl.IndexAccessControl(entry.getValue(), fieldPermissions, roleQueries));
}
return unmodifiableMap(indexPermissions);
}
class Group {
public static class Group {
private final IndexPrivilege privilege;
private final Predicate<String> actionMatcher;
private final String[] indices;
@ -332,14 +168,14 @@ public interface IndicesPermission extends Permission, Iterable<IndicesPermissio
}
private final FieldPermissions fieldPermissions;
private final BytesReference query;
private final Set<BytesReference> query;
public Group(IndexPrivilege privilege, FieldPermissions fieldPermissions, @Nullable BytesReference query, String... indices) {
public Group(IndexPrivilege privilege, FieldPermissions fieldPermissions, @Nullable Set<BytesReference> query, String... indices) {
assert indices.length != 0;
this.privilege = privilege;
this.actionMatcher = privilege.predicate();
this.indices = indices;
this.indexNameMatcher = new AutomatonPredicate(Automatons.patterns(indices));
this.indexNameMatcher = Automatons.predicate(indices);
this.fieldPermissions = Objects.requireNonNull(fieldPermissions);
this.query = query;
}
@ -353,7 +189,7 @@ public interface IndicesPermission extends Permission, Iterable<IndicesPermissio
}
@Nullable
public BytesReference getQuery() {
public Set<BytesReference> getQuery() {
return query;
}
@ -366,7 +202,7 @@ public interface IndicesPermission extends Permission, Iterable<IndicesPermissio
return check(action) && indexNameMatcher.test(index);
}
public boolean hasQuery() {
boolean hasQuery() {
return query != null;
}
}

View File

@ -1,32 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.Privilege.Name;
import org.elasticsearch.xpack.security.support.MetadataUtils;
/**
* Role for users that should be allowed to use the Add Data/Ingest features in the UI
*/
public class IngestAdminRole extends Role {
private static final String[] CLUSTER_PRIVILEGES = new String[] { "manage_index_templates", "manage_pipeline" };
private static final RoleDescriptor.IndicesPrivileges[] INDICES_PRIVILEGES = new RoleDescriptor.IndicesPrivileges[0];
public static final String NAME = "ingest_admin";
public static final RoleDescriptor DESCRIPTOR =
new RoleDescriptor(NAME, CLUSTER_PRIVILEGES, INDICES_PRIVILEGES, null, MetadataUtils.DEFAULT_RESERVED_METADATA);
public static final IngestAdminRole INSTANCE = new IngestAdminRole();
private IngestAdminRole() {
super(DESCRIPTOR.getName(),
new ClusterPermission.Core(ClusterPrivilege.get(new Name(DESCRIPTOR.getClusterPrivileges()))),
new IndicesPermission.Core(Role.Builder.convertFromIndicesPrivileges(DESCRIPTOR.getIndicesPrivileges())),
RunAsPermission.Core.NONE);
}
}

View File

@ -1,31 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.xpack.monitoring.action.MonitoringBulkAction;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.Privilege.Name;
import org.elasticsearch.xpack.security.support.MetadataUtils;
public class KibanaRole extends Role {
private static final String[] CLUSTER_PRIVILEGES = new String[] { "monitor", MonitoringBulkAction.NAME};
private static final RoleDescriptor.IndicesPrivileges[] INDICES_PRIVILEGES = new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices(".kibana*", ".reporting-*").privileges("all").build() };
public static final String NAME = "kibana";
public static final RoleDescriptor DESCRIPTOR =
new RoleDescriptor(NAME, CLUSTER_PRIVILEGES, INDICES_PRIVILEGES, null, MetadataUtils.DEFAULT_RESERVED_METADATA);
public static final KibanaRole INSTANCE = new KibanaRole();
private KibanaRole() {
super(DESCRIPTOR.getName(),
new ClusterPermission.Core(ClusterPrivilege.get(new Name(DESCRIPTOR.getClusterPrivileges()))),
new IndicesPermission.Core(Role.Builder.convertFromIndicesPrivileges(DESCRIPTOR.getIndicesPrivileges())),
RunAsPermission.Core.NONE);
}
}

View File

@ -1,30 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.Privilege.Name;
import org.elasticsearch.xpack.security.support.MetadataUtils;
public class KibanaUserRole extends Role {
private static final String[] CLUSTER_PRIVILEGES = new String[] { "monitor" };
private static final RoleDescriptor.IndicesPrivileges[] INDICES_PRIVILEGES = new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices(".kibana*").privileges("manage", "read", "index", "delete").build() };
public static final String NAME = "kibana_user";
public static final RoleDescriptor DESCRIPTOR =
new RoleDescriptor(NAME, CLUSTER_PRIVILEGES, INDICES_PRIVILEGES, null, MetadataUtils.DEFAULT_RESERVED_METADATA);
public static final KibanaUserRole INSTANCE = new KibanaUserRole();
private KibanaUserRole() {
super(DESCRIPTOR.getName(),
new ClusterPermission.Core(ClusterPrivilege.get(new Name(DESCRIPTOR.getClusterPrivileges()))),
new IndicesPermission.Core(Role.Builder.convertFromIndicesPrivileges(DESCRIPTOR.getIndicesPrivileges())),
RunAsPermission.Core.NONE);
}
}

View File

@ -1,29 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.xpack.monitoring.action.MonitoringBulkAction;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.Privilege.Name;
import org.elasticsearch.xpack.security.support.MetadataUtils;
public class LogstashSystemRole extends Role {
private static final String[] CLUSTER_PRIVILEGES = new String[] { "monitor", MonitoringBulkAction.NAME};
public static final String NAME = "logstash_system";
public static final RoleDescriptor DESCRIPTOR =
new RoleDescriptor(NAME, CLUSTER_PRIVILEGES, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA);
public static final LogstashSystemRole INSTANCE = new LogstashSystemRole();
private LogstashSystemRole() {
super(DESCRIPTOR.getName(),
new ClusterPermission.Core(ClusterPrivilege.get(new Name(DESCRIPTOR.getClusterPrivileges()))),
new IndicesPermission.Core(Builder.convertFromIndicesPrivileges(DESCRIPTOR.getIndicesPrivileges())),
RunAsPermission.Core.NONE);
}
}

View File

@ -1,34 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.ClusterPermission.Core;
import org.elasticsearch.xpack.security.support.MetadataUtils;
/**
* A built-in role that grants users the necessary privileges to use Monitoring. The user will also need the {@link KibanaUserRole}
*/
public class MonitoringUserRole extends Role {
private static final RoleDescriptor.IndicesPrivileges[] INDICES_PRIVILEGES = new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder()
.indices(".marvel-es-*", ".monitoring-*")
.privileges("read")
.build() };
public static final String NAME = "monitoring_user";
public static final RoleDescriptor DESCRIPTOR =
new RoleDescriptor(NAME, null, INDICES_PRIVILEGES, null, MetadataUtils.DEFAULT_RESERVED_METADATA);
public static final MonitoringUserRole INSTANCE = new MonitoringUserRole();
private MonitoringUserRole() {
super(DESCRIPTOR.getName(),
Core.NONE,
new IndicesPermission.Core(Role.Builder.convertFromIndicesPrivileges(DESCRIPTOR.getIndicesPrivileges())),
RunAsPermission.Core.NONE);
}
}

View File

@ -1,15 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
/**
* Represents a permission in the system.
*/
public interface Permission {
boolean isEmpty();
}

View File

@ -1,36 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.Privilege.Name;
import org.elasticsearch.xpack.security.support.MetadataUtils;
/**
* Built-in role that grants the necessary privileges for a remote monitoring agent.
*/
public class RemoteMonitoringAgentRole extends Role {
private static final String[] CLUSTER_PRIVILEGES = new String[] { "manage_index_templates", "manage_ingest_pipelines", "monitor" };
private static final RoleDescriptor.IndicesPrivileges[] INDICES_PRIVILEGES = new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder()
.indices(".marvel-es-*", ".monitoring-*")
.privileges("all")
.build() };
public static final String NAME = "remote_monitoring_agent";
public static final RoleDescriptor DESCRIPTOR =
new RoleDescriptor(NAME, CLUSTER_PRIVILEGES, INDICES_PRIVILEGES, null, MetadataUtils.DEFAULT_RESERVED_METADATA);
public static final RemoteMonitoringAgentRole INSTANCE = new RemoteMonitoringAgentRole();
private RemoteMonitoringAgentRole() {
super(DESCRIPTOR.getName(),
new ClusterPermission.Core(ClusterPrivilege.get(new Name(DESCRIPTOR.getClusterPrivileges()))),
new IndicesPermission.Core(Role.Builder.convertFromIndicesPrivileges(DESCRIPTOR.getIndicesPrivileges())),
RunAsPermission.Core.NONE);
}
}

View File

@ -1,34 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.ClusterPermission.Core;
import org.elasticsearch.xpack.security.support.MetadataUtils;
/**
* A role for users of the reporting features in xpack
*/
public class ReportingUserRole extends Role {
private static final RoleDescriptor.IndicesPrivileges[] INDICES_PRIVILEGES = new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder()
.indices(".reporting-*")
.privileges("read", "write")
.build()
};
public static final String NAME = "reporting_user";
public static final RoleDescriptor DESCRIPTOR =
new RoleDescriptor(NAME, null, INDICES_PRIVILEGES, null, MetadataUtils.DEFAULT_RESERVED_METADATA);
public static final ReportingUserRole INSTANCE = new ReportingUserRole();
private ReportingUserRole() {
super(DESCRIPTOR.getName(),
Core.NONE,
new IndicesPermission.Core(Role.Builder.convertFromIndicesPrivileges(DESCRIPTOR.getIndicesPrivileges())),
RunAsPermission.Core.NONE);
}
}

View File

@ -5,110 +5,155 @@
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl.IndexAccessControl;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.GeneralPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.Privilege;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
public class Role extends GlobalPermission {
public final class Role {
public static final Role EMPTY = Role.builder("__empty").build();
private final String name;
private final ClusterPermission cluster;
private final IndicesPermission indices;
private final RunAsPermission runAs;
Role(String name, ClusterPermission.Core cluster, IndicesPermission.Core indices, RunAsPermission.Core runAs) {
super(cluster, indices, runAs);
Role(String name, ClusterPermission cluster, IndicesPermission indices, RunAsPermission runAs) {
this.name = name;
this.cluster = Objects.requireNonNull(cluster);
this.indices = Objects.requireNonNull(indices);
this.runAs = Objects.requireNonNull(runAs);
}
public String name() {
return name;
}
@Override
public ClusterPermission.Core cluster() {
return (ClusterPermission.Core) super.cluster();
public ClusterPermission cluster() {
return cluster;
}
@Override
public IndicesPermission.Core indices() {
return (IndicesPermission.Core) super.indices();
public IndicesPermission indices() {
return indices;
}
@Override
public RunAsPermission.Core runAs() {
return (RunAsPermission.Core) super.runAs();
public RunAsPermission runAs() {
return runAs;
}
public static Builder builder(String name) {
return new Builder(name);
return new Builder(name, null);
}
public static Builder builder(RoleDescriptor rd) {
return new Builder(rd);
public static Builder builder(String name, FieldPermissionsCache fieldPermissionsCache) {
return new Builder(name, fieldPermissionsCache);
}
public static Builder builder(RoleDescriptor rd, FieldPermissionsCache fieldPermissionsCache) {
return new Builder(rd, fieldPermissionsCache);
}
/**
* Returns whether at least one group encapsulated by this indices permissions is authorized to execute the
* 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,
FieldPermissionsCache fieldPermissionsCache) {
Map<String, IndexAccessControl> indexPermissions = indices.authorize(
action, requestedIndicesOrAliases, metaData, fieldPermissionsCache
);
// At least one role / indices permission set need to match with all the requested indices/aliases:
boolean granted = true;
for (Map.Entry<String, IndicesAccessControl.IndexAccessControl> entry : indexPermissions.entrySet()) {
if (!entry.getValue().isGranted()) {
granted = false;
break;
}
}
return new IndicesAccessControl(granted, indexPermissions);
}
public static class Builder {
private final String name;
private ClusterPermission.Core cluster = ClusterPermission.Core.NONE;
private RunAsPermission.Core runAs = RunAsPermission.Core.NONE;
private ClusterPermission cluster = ClusterPermission.NONE;
private RunAsPermission runAs = RunAsPermission.NONE;
private List<IndicesPermission.Group> groups = new ArrayList<>();
private FieldPermissionsCache fieldPermissionsCache = null;
private Builder(String name) {
private Builder(String name, FieldPermissionsCache fieldPermissionsCache) {
this.name = name;
this.fieldPermissionsCache = fieldPermissionsCache;
}
private Builder(RoleDescriptor rd) {
private Builder(RoleDescriptor rd, @Nullable FieldPermissionsCache fieldPermissionsCache) {
this.name = rd.getName();
this.fieldPermissionsCache = fieldPermissionsCache;
if (rd.getClusterPrivileges().length == 0) {
cluster = ClusterPermission.Core.NONE;
cluster = ClusterPermission.NONE;
} else {
this.cluster(ClusterPrivilege.get((new Privilege.Name(rd.getClusterPrivileges()))));
this.cluster(ClusterPrivilege.get(Sets.newHashSet(rd.getClusterPrivileges())));
}
groups.addAll(convertFromIndicesPrivileges(rd.getIndicesPrivileges()));
groups.addAll(convertFromIndicesPrivileges(rd.getIndicesPrivileges(), fieldPermissionsCache));
String[] rdRunAs = rd.getRunAs();
if (rdRunAs != null && rdRunAs.length > 0) {
this.runAs(new GeneralPrivilege(new Privilege.Name(rdRunAs), rdRunAs));
this.runAs(new Privilege(Sets.newHashSet(rdRunAs), rdRunAs));
}
}
// FIXME we should throw an exception if we have already set cluster or runAs...
public Builder cluster(ClusterPrivilege privilege) {
cluster = new ClusterPermission.Core(privilege);
cluster = new ClusterPermission(privilege);
return this;
}
public Builder runAs(GeneralPrivilege privilege) {
runAs = new RunAsPermission.Core(privilege);
public Builder runAs(Privilege privilege) {
runAs = new RunAsPermission(privilege);
return this;
}
public Builder add(IndexPrivilege privilege, String... indices) {
groups.add(new IndicesPermission.Group(privilege, new FieldPermissions(), null, indices));
groups.add(new IndicesPermission.Group(privilege, FieldPermissions.DEFAULT, null, indices));
return this;
}
public Builder add(FieldPermissions fieldPermissions, BytesReference query, IndexPrivilege privilege, String... indices) {
public Builder add(FieldPermissions fieldPermissions, Set<BytesReference> query, IndexPrivilege privilege, String... indices) {
groups.add(new IndicesPermission.Group(privilege, fieldPermissions, query, indices));
return this;
}
public Role build() {
IndicesPermission.Core indices = groups.isEmpty() ? IndicesPermission.Core.NONE :
new IndicesPermission.Core(groups.toArray(new IndicesPermission.Group[groups.size()]));
IndicesPermission indices = groups.isEmpty() ? IndicesPermission.NONE :
new IndicesPermission(groups.toArray(new IndicesPermission.Group[groups.size()]));
return new Role(name, cluster, indices, runAs);
}
static List<IndicesPermission.Group> convertFromIndicesPrivileges(RoleDescriptor.IndicesPrivileges[] indicesPrivileges) {
static List<IndicesPermission.Group> convertFromIndicesPrivileges(RoleDescriptor.IndicesPrivileges[] indicesPrivileges,
@Nullable FieldPermissionsCache fieldPermissionsCache) {
List<IndicesPermission.Group> list = new ArrayList<>(indicesPrivileges.length);
for (RoleDescriptor.IndicesPrivileges privilege : indicesPrivileges) {
list.add(new IndicesPermission.Group(IndexPrivilege.get(new Privilege.Name(privilege.getPrivileges())),
privilege.getFieldPermissions(),
privilege.getQuery(),
final FieldPermissions fieldPermissions = fieldPermissionsCache != null ?
fieldPermissionsCache.getFieldPermissions(privilege.getGrantedFields(), privilege.getDeniedFields()) :
new FieldPermissions(privilege.getGrantedFields(), privilege.getDeniedFields());
final Set<BytesReference> query = privilege.getQuery() == null ? null : Collections.singleton(privilege.getQuery());
list.add(new IndicesPermission.Group(IndexPrivilege.get(Sets.newHashSet(privilege.getPrivileges())),
fieldPermissions,
query,
privilege.getIndices()));
}

View File

@ -5,76 +5,28 @@
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.xpack.security.authz.privilege.GeneralPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.Privilege;
import java.util.List;
import java.util.function.Predicate;
/**
* A permissions that is based on a general privilege that contains patterns of users that this
* user can execute a request as
*/
public interface RunAsPermission extends Permission {
public final class RunAsPermission {
public static final RunAsPermission NONE = new RunAsPermission(Privilege.NONE);
private final Predicate<String> predicate;
RunAsPermission(Privilege privilege) {
this.predicate = privilege.predicate();
}
/**
* Checks if this permission grants run as to the specified user
*/
boolean check(String username);
class Core implements RunAsPermission {
public static final Core NONE = new Core(GeneralPrivilege.NONE);
private final GeneralPrivilege privilege;
private final Predicate<String> predicate;
public Core(GeneralPrivilege privilege) {
this.privilege = privilege;
this.predicate = privilege.predicate();
}
@Override
public boolean check(String username) {
return predicate.test(username);
}
@Override
public boolean isEmpty() {
return this == NONE;
}
}
class Globals implements RunAsPermission {
private final List<GlobalPermission> globals;
public Globals(List<GlobalPermission> globals) {
this.globals = globals;
}
@Override
public boolean check(String username) {
if (globals == null) {
return false;
}
for (GlobalPermission global : globals) {
if (global.runAs().check(username)) {
return true;
}
}
return false;
}
@Override
public boolean isEmpty() {
if (globals == null || globals.isEmpty()) {
return true;
}
for (GlobalPermission global : globals) {
if (!global.isEmpty()) {
return false;
}
}
return true;
}
public boolean check(String username) {
return predicate.test(username);
}
}

View File

@ -1,30 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.GeneralPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.Privilege.Name;
import org.elasticsearch.xpack.security.support.MetadataUtils;
public class SuperuserRole extends Role {
public static final String NAME = "superuser";
public static final RoleDescriptor DESCRIPTOR = new RoleDescriptor(NAME, new String[] { "all" },
new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices("*").privileges("all").build()},
new String[] { "*" },
MetadataUtils.DEFAULT_RESERVED_METADATA);
public static final SuperuserRole INSTANCE = new SuperuserRole();
private SuperuserRole() {
super(DESCRIPTOR.getName(),
new ClusterPermission.Core(ClusterPrivilege.get(new Name(DESCRIPTOR.getClusterPrivileges()))),
new IndicesPermission.Core(Role.Builder.convertFromIndicesPrivileges(DESCRIPTOR.getIndicesPrivileges())),
new RunAsPermission.Core(GeneralPrivilege.ALL));
}
}

View File

@ -1,30 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.Privilege.Name;
import org.elasticsearch.xpack.security.support.MetadataUtils;
/**
* Reserved role for the transport client
*/
public class TransportClientRole extends Role {
public static final String NAME = "transport_client";
private static final String[] CLUSTER_PRIVILEGES = new String[] { "transport_client" };
public static final RoleDescriptor DESCRIPTOR =
new RoleDescriptor(NAME, CLUSTER_PRIVILEGES, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA);
public static final TransportClientRole INSTANCE = new TransportClientRole();
private TransportClientRole() {
super(DESCRIPTOR.getName(),
new ClusterPermission.Core(ClusterPrivilege.get(new Name(DESCRIPTOR.getClusterPrivileges()))),
IndicesPermission.Core.NONE, RunAsPermission.Core.NONE);
}
}

View File

@ -1,67 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.privilege;
import org.apache.lucene.util.automaton.Automaton;
import org.elasticsearch.xpack.security.support.AutomatonPredicate;
import org.elasticsearch.xpack.security.support.Automatons;
import java.util.function.Predicate;
import static org.apache.lucene.util.automaton.Operations.subsetOf;
import static org.elasticsearch.xpack.security.support.Automatons.patterns;
@SuppressWarnings("unchecked")
abstract class AbstractAutomatonPrivilege<P extends AbstractAutomatonPrivilege<P>> extends Privilege<P> {
protected final Automaton automaton;
AbstractAutomatonPrivilege(String name, String... patterns) {
super(new Name(name));
this.automaton = patterns(patterns);
}
AbstractAutomatonPrivilege(Name name, String... patterns) {
super(name);
this.automaton = patterns(patterns);
}
AbstractAutomatonPrivilege(Name name, Automaton automaton) {
super(name);
this.automaton = automaton;
}
@Override
public Predicate<String> predicate() {
return new AutomatonPredicate(automaton);
}
protected P plus(P other) {
if (other.implies((P) this)) {
return other;
}
if (this.implies(other)) {
return (P) this;
}
return create(name.add(other.name), Automatons.unionAndDeterminize(automaton, other.automaton));
}
@Override
public boolean implies(P other) {
return subsetOf(other.automaton, automaton);
}
@Override
public String toString() {
return name.toString();
}
protected abstract P create(Name name, Automaton automaton);
protected abstract P none();
}

View File

@ -7,30 +7,33 @@ package org.elasticsearch.xpack.security.authz.privilege;
import org.apache.lucene.util.automaton.Automaton;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.xpack.security.support.Automatons;
import java.util.Collections;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Predicate;
import static org.elasticsearch.xpack.security.support.Automatons.minusAndDeterminize;
import static org.elasticsearch.xpack.security.support.Automatons.minusAndMinimize;
import static org.elasticsearch.xpack.security.support.Automatons.patterns;
public class ClusterPrivilege extends AbstractAutomatonPrivilege<ClusterPrivilege> {
public final class ClusterPrivilege extends Privilege {
// shared automatons
private static final Automaton MANAGE_SECURITY_AUTOMATON = patterns("cluster:admin/xpack/security/*");
private static final Automaton MONITOR_AUTOMATON = patterns("cluster:monitor/*");
private static final Automaton ALL_CLUSTER_AUTOMATON = patterns("cluster:*", "indices:admin/template/*");
private static final Automaton MANAGE_AUTOMATON = minusAndDeterminize(ALL_CLUSTER_AUTOMATON, MANAGE_SECURITY_AUTOMATON);
private static final Automaton MANAGE_AUTOMATON = minusAndMinimize(ALL_CLUSTER_AUTOMATON, MANAGE_SECURITY_AUTOMATON);
private static final Automaton TRANSPORT_CLIENT_AUTOMATON = patterns("cluster:monitor/nodes/liveness", "cluster:monitor/state");
private static final Automaton MANAGE_IDX_TEMPLATE_AUTOMATON = patterns("indices:admin/template/*");
private static final Automaton MANAGE_INGEST_PIPELINE_AUTOMATON = patterns("cluster:admin/ingest/pipeline/*");
public static final ClusterPrivilege NONE = new ClusterPrivilege(Name.NONE, Automatons.EMPTY);
public static final ClusterPrivilege ALL = new ClusterPrivilege(Name.ALL, ALL_CLUSTER_AUTOMATON);
public static final ClusterPrivilege NONE = new ClusterPrivilege("none", Automatons.EMPTY);
public static final ClusterPrivilege ALL = new ClusterPrivilege("all", ALL_CLUSTER_AUTOMATON);
public static final ClusterPrivilege MONITOR = new ClusterPrivilege("monitor", MONITOR_AUTOMATON);
public static final ClusterPrivilege MANAGE = new ClusterPrivilege("manage", MANAGE_AUTOMATON);
public static final ClusterPrivilege MANAGE_IDX_TEMPLATES =
@ -43,89 +46,69 @@ public class ClusterPrivilege extends AbstractAutomatonPrivilege<ClusterPrivileg
public static final Predicate<String> ACTION_MATCHER = ClusterPrivilege.ALL.predicate();
private static final Set<ClusterPrivilege> values = new CopyOnWriteArraySet<>();
private static final Map<String, ClusterPrivilege> VALUES = MapBuilder.<String, ClusterPrivilege>newMapBuilder()
.put("none", NONE)
.put("all", ALL)
.put("monitor", MONITOR)
.put("manage", MANAGE)
.put("manage_index_templates", MANAGE_IDX_TEMPLATES)
.put("manage_ingest_pipelines", MANAGE_INGEST_PIPELINES)
.put("transport_client", TRANSPORT_CLIENT)
.put("manage_security", MANAGE_SECURITY)
.put("manage_pipeline", MANAGE_PIPELINE)
.immutableMap();
static {
values.add(NONE);
values.add(ALL);
values.add(MONITOR);
values.add(MANAGE);
values.add(MANAGE_IDX_TEMPLATES);
values.add(MANAGE_INGEST_PIPELINES);
values.add(TRANSPORT_CLIENT);
values.add(MANAGE_SECURITY);
values.add(MANAGE_PIPELINE);
}
static Set<ClusterPrivilege> values() {
return values;
}
private static final ConcurrentHashMap<Name, ClusterPrivilege> cache = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<Set<String>, ClusterPrivilege> CACHE = new ConcurrentHashMap<>();
private ClusterPrivilege(String name, String... patterns) {
super(name, patterns);
}
private ClusterPrivilege(String name, Automaton automaton) {
super(new Name(name), automaton);
super(Collections.singleton(name), automaton);
}
private ClusterPrivilege(Name name, Automaton automaton) {
private ClusterPrivilege(Set<String> name, Automaton automaton) {
super(name, automaton);
}
public static void addCustom(String name, String... actionPatterns) {
for (String pattern : actionPatterns) {
if (!ClusterPrivilege.ACTION_MATCHER.test(pattern)) {
throw new IllegalArgumentException("cannot register custom cluster privilege [" + name + "]. " +
"cluster action must follow the 'cluster:*' format");
public static ClusterPrivilege get(Set<String> name) {
if (name == null || name.isEmpty()) {
return NONE;
}
return CACHE.computeIfAbsent(name, ClusterPrivilege::resolve);
}
private static ClusterPrivilege resolve(Set<String> name) {
final int size = name.size();
if (size == 0) {
throw new IllegalArgumentException("empty set should not be used");
}
Set<String> actions = new HashSet<>();
Set<Automaton> automata = new HashSet<>();
for (String part : name) {
part = part.toLowerCase(Locale.ROOT);
if (ACTION_MATCHER.test(part)) {
actions.add(actionToPattern(part));
} else {
ClusterPrivilege privilege = VALUES.get(part);
if (privilege != null && size == 1) {
return privilege;
} else if (privilege != null) {
automata.add(privilege.automaton);
} else {
throw new IllegalArgumentException("unknown cluster privilege [" + name + "]. a privilege must be either " +
"one of the predefined fixed cluster privileges [" +
Strings.collectionToCommaDelimitedString(VALUES.entrySet()) + "] or a pattern over one of the available " +
"cluster actions");
}
}
}
ClusterPrivilege custom = new ClusterPrivilege(name, actionPatterns);
if (values.contains(custom)) {
throw new IllegalArgumentException("cannot register custom cluster privilege [" + name + "] as it already exists.");
if (actions.isEmpty() == false) {
automata.add(patterns(actions));
}
values.add(custom);
}
@Override
protected ClusterPrivilege create(Name name, Automaton automaton) {
return new ClusterPrivilege(name, automaton);
}
@Override
protected ClusterPrivilege none() {
return NONE;
}
public static ClusterPrivilege action(String action) {
String pattern = actionToPattern(action);
return new ClusterPrivilege(action, pattern);
}
public static ClusterPrivilege get(Name name) {
return cache.computeIfAbsent(name, (theName) -> {
ClusterPrivilege cluster = NONE;
for (String part : theName.parts) {
cluster = cluster == NONE ? resolve(part) : cluster.plus(resolve(part));
}
return cluster;
});
}
private static ClusterPrivilege resolve(String name) {
name = name.toLowerCase(Locale.ROOT);
if (ACTION_MATCHER.test(name)) {
return action(name);
}
for (ClusterPrivilege cluster : values) {
if (name.equals(cluster.name.toString())) {
return cluster;
}
}
throw new IllegalArgumentException("unknown cluster privilege [" + name + "]. a privilege must be either " +
"one of the predefined fixed cluster privileges [" + Strings.collectionToCommaDelimitedString(values) +
"] or a pattern over one of the available cluster actions");
return new ClusterPrivilege(name, Automatons.unionAndMinimize(automata));
}
}

View File

@ -1,37 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.privilege;
import org.apache.lucene.util.automaton.Automaton;
import org.elasticsearch.xpack.security.support.Automatons;
public class GeneralPrivilege extends AbstractAutomatonPrivilege<GeneralPrivilege> {
public static final GeneralPrivilege NONE = new GeneralPrivilege(Name.NONE, Automatons.EMPTY);
public static final GeneralPrivilege ALL = new GeneralPrivilege(Name.ALL, Automatons.MATCH_ALL);
public GeneralPrivilege(String name, String... patterns) {
super(name, patterns);
}
public GeneralPrivilege(Name name, String... patterns) {
super(name, patterns);
}
public GeneralPrivilege(Name name, Automaton automaton) {
super(name, automaton);
}
@Override
protected GeneralPrivilege create(Name name, Automaton automaton) {
return new GeneralPrivilege(name, automaton);
}
@Override
protected GeneralPrivilege none() {
return NONE;
}
}

View File

@ -5,7 +5,7 @@
*/
package org.elasticsearch.xpack.security.authz.privilege;
public class HealthAndStatsPrivilege extends GeneralPrivilege {
public final class HealthAndStatsPrivilege extends Privilege {
public static final HealthAndStatsPrivilege INSTANCE = new HealthAndStatsPrivilege();

View File

@ -20,18 +20,22 @@ import org.elasticsearch.action.admin.indices.mapping.put.PutMappingAction;
import org.elasticsearch.action.admin.indices.settings.get.GetSettingsAction;
import org.elasticsearch.action.admin.indices.validate.query.ValidateQueryAction;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.xpack.security.support.Automatons;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Predicate;
import static org.elasticsearch.xpack.security.support.Automatons.patterns;
import static org.elasticsearch.xpack.security.support.Automatons.unionAndDeterminize;
import static org.elasticsearch.xpack.security.support.Automatons.unionAndMinimize;
public class IndexPrivilege extends AbstractAutomatonPrivilege<IndexPrivilege> {
public final class IndexPrivilege extends Privilege {
private static final Automaton ALL_AUTOMATON = patterns("indices:*");
private static final Automaton READ_AUTOMATON = patterns("indices:data/read/*");
@ -41,15 +45,16 @@ public class IndexPrivilege extends AbstractAutomatonPrivilege<IndexPrivilege> {
private static final Automaton DELETE_AUTOMATON = patterns("indices:data/write/delete*");
private static final Automaton WRITE_AUTOMATON = patterns("indices:data/write/*", PutMappingAction.NAME);
private static final Automaton MONITOR_AUTOMATON = patterns("indices:monitor/*");
private static final Automaton MANAGE_AUTOMATON = unionAndDeterminize(MONITOR_AUTOMATON, patterns("indices:admin/*"));
private static final Automaton MANAGE_AUTOMATON =
unionAndMinimize(Arrays.asList(MONITOR_AUTOMATON, patterns("indices:admin/*")));
private static final Automaton CREATE_INDEX_AUTOMATON = patterns(CreateIndexAction.NAME);
private static final Automaton DELETE_INDEX_AUTOMATON = patterns(DeleteIndexAction.NAME);
private static final Automaton VIEW_METADATA_AUTOMATON = patterns(GetAliasesAction.NAME, AliasesExistAction.NAME,
GetIndexAction.NAME, IndicesExistsAction.NAME, GetFieldMappingsAction.NAME + "*", GetMappingsAction.NAME,
ClusterSearchShardsAction.NAME, TypesExistsAction.NAME, ValidateQueryAction.NAME + "*", GetSettingsAction.NAME);
public static final IndexPrivilege NONE = new IndexPrivilege(Name.NONE, Automatons.EMPTY);
public static final IndexPrivilege ALL = new IndexPrivilege(Name.ALL, ALL_AUTOMATON);
public static final IndexPrivilege NONE = new IndexPrivilege("none", Automatons.EMPTY);
public static final IndexPrivilege ALL = new IndexPrivilege("all", ALL_AUTOMATON);
public static final IndexPrivilege READ = new IndexPrivilege("read", READ_AUTOMATON);
public static final IndexPrivilege CREATE = new IndexPrivilege("create", CREATE_AUTOMATON);
public static final IndexPrivilege INDEX = new IndexPrivilege("index", INDEX_AUTOMATON);
@ -61,106 +66,78 @@ public class IndexPrivilege extends AbstractAutomatonPrivilege<IndexPrivilege> {
public static final IndexPrivilege CREATE_INDEX = new IndexPrivilege("create_index", CREATE_INDEX_AUTOMATON);
public static final IndexPrivilege VIEW_METADATA = new IndexPrivilege("view_index_metadata", VIEW_METADATA_AUTOMATON);
private static final Set<IndexPrivilege> values = new CopyOnWriteArraySet<>();
static {
values.add(NONE);
values.add(ALL);
values.add(MANAGE);
values.add(CREATE_INDEX);
values.add(MONITOR);
values.add(READ);
values.add(INDEX);
values.add(DELETE);
values.add(WRITE);
values.add(CREATE);
values.add(DELETE_INDEX);
values.add(VIEW_METADATA);
}
private static final Map<String, IndexPrivilege> VALUES = MapBuilder.<String, IndexPrivilege>newMapBuilder()
.put("none", NONE)
.put("all", ALL)
.put("manage", MANAGE)
.put("create_index", CREATE_INDEX)
.put("monitor", MONITOR)
.put("read", READ)
.put("index", INDEX)
.put("delete", DELETE)
.put("write", WRITE)
.put("create", CREATE)
.put("delete_index", DELETE_INDEX)
.put("view_index_metadata", VIEW_METADATA)
.immutableMap();
public static final Predicate<String> ACTION_MATCHER = ALL.predicate();
public static final Predicate<String> CREATE_INDEX_MATCHER = CREATE_INDEX.predicate();
static Set<IndexPrivilege> values() {
return values;
}
private static final ConcurrentHashMap<Name, IndexPrivilege> cache = new ConcurrentHashMap<>();
private IndexPrivilege(String name, String... patterns) {
super(name, patterns);
}
private static final ConcurrentHashMap<Set<String>, IndexPrivilege> CACHE = new ConcurrentHashMap<>();
private IndexPrivilege(String name, Automaton automaton) {
super(new Name(name), automaton);
super(Collections.singleton(name), automaton);
}
private IndexPrivilege(Name name, Automaton automaton) {
private IndexPrivilege(Set<String> name, Automaton automaton) {
super(name, automaton);
}
public static void addCustom(String name, String... actionPatterns) {
for (String pattern : actionPatterns) {
if (!IndexPrivilege.ACTION_MATCHER.test(pattern)) {
throw new IllegalArgumentException("cannot register custom index privilege [" + name + "]." +
" index action must follow the 'indices:*' format");
public static IndexPrivilege get(Set<String> name) {
return CACHE.computeIfAbsent(name, (theName) -> {
if (theName.isEmpty()) {
return NONE;
} else {
return resolve(theName);
}
}
IndexPrivilege custom = new IndexPrivilege(name, actionPatterns);
if (values.contains(custom)) {
throw new IllegalArgumentException("cannot register custom index privilege [" + name + "] as it already exists.");
}
values.add(custom);
}
@Override
protected IndexPrivilege create(Name name, Automaton automaton) {
if (name == Name.NONE) {
return NONE;
}
return new IndexPrivilege(name, automaton);
}
@Override
protected IndexPrivilege none() {
return NONE;
}
public static IndexPrivilege action(String action) {
return new IndexPrivilege(action, actionToPattern(action));
}
public static IndexPrivilege get(Name name) {
return cache.computeIfAbsent(name, (theName) -> {
IndexPrivilege index = NONE;
for (String part : theName.parts) {
index = index == NONE ? resolve(part) : index.plus(resolve(part));
}
return index;
});
}
public static IndexPrivilege union(IndexPrivilege... indices) {
IndexPrivilege result = NONE;
for (IndexPrivilege index : indices) {
result = result.plus(index);
private static IndexPrivilege resolve(Set<String> name) {
final int size = name.size();
if (size == 0) {
throw new IllegalArgumentException("empty set should not be used");
}
return result;
}
private static IndexPrivilege resolve(String name) {
name = name.toLowerCase(Locale.ROOT);
if (ACTION_MATCHER.test(name)) {
return action(name);
}
for (IndexPrivilege index : values) {
if (name.toLowerCase(Locale.ROOT).equals(index.name.toString())) {
return index;
Set<String> actions = new HashSet<>();
Set<Automaton> automata = new HashSet<>();
for (String part : name) {
part = part.toLowerCase(Locale.ROOT);
if (ACTION_MATCHER.test(part)) {
actions.add(actionToPattern(part));
} else {
IndexPrivilege indexPrivilege = VALUES.get(part);
if (indexPrivilege != null && size == 1) {
return indexPrivilege;
} else if (indexPrivilege != null) {
automata.add(indexPrivilege.automaton);
} else {
throw new IllegalArgumentException("unknown index privilege [" + name + "]. a privilege must be either " +
"one of the predefined fixed indices privileges [" +
Strings.collectionToCommaDelimitedString(VALUES.entrySet()) + "] or a pattern over one of the available index" +
" actions");
}
}
}
throw new IllegalArgumentException("unknown index privilege [" + name + "]. a privilege must be either " +
"one of the predefined fixed indices privileges [" + Strings.collectionToCommaDelimitedString(values) +
"] or a pattern over one of the available index actions");
if (actions.isEmpty() == false) {
automata.add(patterns(actions));
}
return new IndexPrivilege(name, Automatons.unionAndMinimize(automata));
}
static Map<String, IndexPrivilege> values() {
return VALUES;
}
}

View File

@ -5,36 +5,44 @@
*/
package org.elasticsearch.xpack.security.authz.privilege;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.util.set.Sets;
import org.apache.lucene.util.automaton.Automaton;
import org.elasticsearch.xpack.security.support.Automatons;
import java.util.HashSet;
import java.util.Collections;
import java.util.Set;
import java.util.function.Predicate;
import static java.util.Collections.singleton;
import static java.util.Collections.unmodifiableSet;
import static org.elasticsearch.common.util.set.Sets.newHashSet;
import static org.elasticsearch.xpack.security.support.Automatons.patterns;
public abstract class Privilege<P extends Privilege<P>> {
public class Privilege {
protected final Name name;
public static final Privilege NONE = new Privilege(Collections.singleton("none"), Automatons.EMPTY);
public static final Privilege ALL = new Privilege(Collections.singleton("all"), Automatons.MATCH_ALL);
Privilege(Name name) {
this.name = name;
protected final Set<String> name;
protected final Automaton automaton;
protected final Predicate<String> predicate;
public Privilege(String name, String... patterns) {
this(Collections.singleton(name), patterns);
}
public Name name() {
public Privilege(Set<String> name, String... patterns) {
this(name, patterns(patterns));
}
public Privilege(Set<String> name, Automaton automaton) {
this.name = name;
this.automaton = automaton;
this.predicate = Automatons.predicate(automaton);
}
public Set<String> name() {
return name;
}
public abstract Predicate<String> predicate();
public abstract boolean implies(P other);
@SuppressWarnings("unchecked")
public boolean isAlias(P other) {
return this.implies(other) && other.implies((P) this);
public Predicate<String> predicate() {
return predicate;
}
@Override
@ -58,54 +66,12 @@ public abstract class Privilege<P extends Privilege<P>> {
return text + "*";
}
public static class Name {
@Override
public String toString() {
return name.toString();
}
public static final Name NONE = new Name("none");
public static final Name ALL = new Name("all");
final Set<String> parts;
public Name(String name) {
assert name != null && !name.contains(",");
parts = singleton(name);
}
public Name(Set<String> parts) {
assert !parts.isEmpty();
this.parts = unmodifiableSet(new HashSet<>(parts));
}
public Name(String... parts) {
this(unmodifiableSet(newHashSet(parts)));
}
@Override
public String toString() {
return Strings.collectionToCommaDelimitedString(parts);
}
public Name add(Name other) {
return new Name(Sets.union(parts, other.parts));
}
public Name remove(Name other) {
Set<String> parts = Sets.difference(this.parts, other.parts);
return parts.isEmpty() ? NONE : new Name(parts);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Name name = (Name) o;
return parts.equals(name.parts);
}
@Override
public int hashCode() {
return parts.hashCode();
}
public Automaton getAutomaton() {
return automaton;
}
}

View File

@ -5,35 +5,29 @@
*/
package org.elasticsearch.xpack.security.authz.privilege;
import org.elasticsearch.xpack.security.support.AutomatonPredicate;
import org.elasticsearch.xpack.security.support.Automatons;
import java.util.Collections;
import java.util.function.Predicate;
import static org.elasticsearch.xpack.security.support.Automatons.patterns;
public class SystemPrivilege extends Privilege<SystemPrivilege> {
public final class SystemPrivilege extends Privilege {
public static SystemPrivilege INSTANCE = new SystemPrivilege();
protected static final Predicate<String> PREDICATE = new AutomatonPredicate(patterns(
private static final Predicate<String> PREDICATE = Automatons.predicate(
"internal:*",
"indices:monitor/*", // added for monitoring
"cluster:monitor/*", // added for monitoring
"cluster:admin/reroute", // added for DiskThresholdDecider.DiskListener
"indices:admin/mapping/put" // needed for recovery and shrink api
));
);
SystemPrivilege() {
super(new Name("internal"));
private SystemPrivilege() {
super(Collections.singleton("internal"));
}
@Override
public Predicate<String> predicate() {
return PREDICATE;
}
@Override
public boolean implies(SystemPrivilege other) {
return true;
}
}

View File

@ -6,12 +6,40 @@
package org.elasticsearch.xpack.security.authz.store;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.cache.Cache;
import org.elasticsearch.common.cache.CacheBuilder;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.internal.Nullable;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
import org.elasticsearch.common.util.concurrent.ReleasableLock;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.RoleDescriptor.IndicesPrivileges;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.Privilege;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.stream.Collectors;
import static org.elasticsearch.xpack.security.Security.setting;
/**
* A composite roles store that combines built in roles, file-based roles, and index-based roles. Checks the built in roles first, then the
@ -19,44 +47,188 @@ import java.util.Map;
*/
public class CompositeRolesStore extends AbstractComponent {
// the lock is used in an odd manner; when iterating over the cache we cannot have modifiers other than deletes using
// the iterator but when not iterating we can modify the cache without external locking. When making normal modifications to the cache
// the read lock is obtained so that we can allow concurrent modifications; however when we need to iterate over the keys or values of
// the cache the write lock must obtained to prevent any modifications
private final ReleasableLock readLock;
private final ReleasableLock writeLock;
{
final ReadWriteLock iterationLock = new ReentrantReadWriteLock();
readLock = new ReleasableLock(iterationLock.readLock());
writeLock = new ReleasableLock(iterationLock.writeLock());
}
public static final Setting<Integer> CACHE_SIZE_SETTING =
Setting.intSetting(setting("authz.store.roles.cache.max_size"), 10000, Property.NodeScope);
private final FileRolesStore fileRolesStore;
private final NativeRolesStore nativeRolesStore;
private final ReservedRolesStore reservedRolesStore;
private final Cache<Set<String>, Role> roleCache;
private final Set<String> negativeLookupCache;
private final AtomicLong numInvalidation = new AtomicLong();
public CompositeRolesStore(Settings settings, FileRolesStore fileRolesStore, NativeRolesStore nativeRolesStore,
ReservedRolesStore reservedRolesStore) {
super(settings);
this.fileRolesStore = fileRolesStore;
// invalidating all on a file based role update is heavy handed to say the least, but in general this should be infrequent so the
// impact isn't really worth the added complexity of only clearing the changed values
fileRolesStore.addListener(this::invalidateAll);
this.nativeRolesStore = nativeRolesStore;
this.reservedRolesStore = reservedRolesStore;
}
private Role getBuildInRole(String role) {
// builtins first
Role builtIn = reservedRolesStore.role(role);
if (builtIn != null) {
logger.trace("loaded role [{}] from reserved roles store", role);
return builtIn;
CacheBuilder<Set<String>, Role> builder = CacheBuilder.builder();
final int cacheSize = CACHE_SIZE_SETTING.get(settings);
if (cacheSize >= 0) {
builder.setMaximumWeight(cacheSize);
}
// Try the file next, then the index if it isn't there
Role fileRole = fileRolesStore.role(role);
if (fileRole != null) {
logger.trace("loaded role [{}] from file roles store", role);
return fileRole;
}
return null;
this.roleCache = builder.build();
this.negativeLookupCache = ConcurrentCollections.newConcurrentSet();
}
public void roles(String role, ActionListener<Role> roleActionListener) {
Role storedRole = getBuildInRole(role);
if (storedRole == null) {
nativeRolesStore.role(role, roleActionListener);
public void roles(Set<String> roleNames, FieldPermissionsCache fieldPermissionsCache, ActionListener<Role> roleActionListener) {
Role existing = roleCache.get(roleNames);
if (existing != null) {
roleActionListener.onResponse(existing);
} else {
roleActionListener.onResponse(storedRole);
final long invalidationCounter = numInvalidation.get();
roleDescriptors(roleNames, ActionListener.wrap(
(descriptors) -> {
final Role role = buildRoleFromDescriptors(descriptors, fieldPermissionsCache);
if (role != null) {
try (ReleasableLock ignored = readLock.acquire()) {
/* this is kinda spooky. We use a read/write lock to ensure we don't modify the cache if we hold the write
* lock (fetching stats for instance - which is kinda overkill?) but since we fetching stuff in an async
* fashion we need to make sure that if the cache got invalidated since we started the request we don't
* put a potential stale result in the cache, hence the numInvalidation.get() comparison to the number of
* invalidation when we started. we just try to be on the safe side and don't cache potentially stale
* results*/
if (invalidationCounter == numInvalidation.get()) {
roleCache.computeIfAbsent(roleNames, (s) -> role);
}
}
}
roleActionListener.onResponse(role);
},
roleActionListener::onFailure));
}
}
private void roleDescriptors(Set<String> roleNames, ActionListener<Set<RoleDescriptor>> roleDescriptorActionListener) {
final Set<String> filteredRoleNames =
roleNames.stream().filter((s) -> negativeLookupCache.contains(s) == false).collect(Collectors.toSet());
final Set<RoleDescriptor> builtInRoleDescriptors = getBuiltInRoleDescriptors(filteredRoleNames);
Set<String> remainingRoleNames = difference(filteredRoleNames, builtInRoleDescriptors);
if (remainingRoleNames.isEmpty()) {
roleDescriptorActionListener.onResponse(Collections.unmodifiableSet(builtInRoleDescriptors));
} else {
nativeRolesStore.getRoleDescriptors(remainingRoleNames.toArray(Strings.EMPTY_ARRAY), ActionListener.wrap((descriptors) -> {
builtInRoleDescriptors.addAll(descriptors);
if (builtInRoleDescriptors.size() != filteredRoleNames.size()) {
final Set<String> missing = difference(filteredRoleNames, builtInRoleDescriptors);
assert missing.isEmpty() == false : "the missing set should not be empty if the sizes didn't match";
negativeLookupCache.addAll(missing);
}
roleDescriptorActionListener.onResponse(Collections.unmodifiableSet(builtInRoleDescriptors));
}, roleDescriptorActionListener::onFailure));
}
}
private Set<RoleDescriptor> getBuiltInRoleDescriptors(Set<String> roleNames) {
final Set<RoleDescriptor> descriptors = reservedRolesStore.roleDescriptors().stream()
.filter((rd) -> roleNames.contains(rd.getName()))
.collect(Collectors.toCollection(HashSet::new));
final Set<String> difference = difference(roleNames, descriptors);
if (difference.isEmpty() == false) {
descriptors.addAll(fileRolesStore.roleDescriptors(difference));
}
return descriptors;
}
private Set<String> difference(Set<String> roleNames, Set<RoleDescriptor> descriptors) {
Set<String> foundNames = descriptors.stream().map(RoleDescriptor::getName).collect(Collectors.toSet());
return Sets.difference(roleNames, foundNames);
}
public static Role buildRoleFromDescriptors(Set<RoleDescriptor> roleDescriptors, FieldPermissionsCache fieldPermissionsCache) {
if (roleDescriptors.isEmpty()) {
return Role.EMPTY;
}
StringBuilder nameBuilder = new StringBuilder();
Set<String> clusterPrivileges = new HashSet<>();
Set<String> runAs = new HashSet<>();
Map<Set<String>, MergableIndicesPrivilege> indicesPrivilegesMap = new HashMap<>();
for (RoleDescriptor descriptor : roleDescriptors) {
nameBuilder.append(descriptor.getName());
nameBuilder.append('_');
if (descriptor.getClusterPrivileges() != null) {
clusterPrivileges.addAll(Arrays.asList(descriptor.getClusterPrivileges()));
}
if (descriptor.getRunAs() != null) {
runAs.addAll(Arrays.asList(descriptor.getRunAs()));
}
IndicesPrivileges[] indicesPrivileges = descriptor.getIndicesPrivileges();
for (IndicesPrivileges indicesPrivilege : indicesPrivileges) {
Set<String> key = Sets.newHashSet(indicesPrivilege.getIndices());
// if a index privilege is an explicit denial, then we treat it as non-existent since we skipped these in the past when
// merging
final boolean isExplicitDenial =
indicesPrivileges.length == 1 && "none".equalsIgnoreCase(indicesPrivilege.getPrivileges()[0]);
if (isExplicitDenial == false) {
indicesPrivilegesMap.compute(key, (k, value) -> {
if (value == null) {
return new MergableIndicesPrivilege(indicesPrivilege.getIndices(), indicesPrivilege.getPrivileges(),
indicesPrivilege.getGrantedFields(), indicesPrivilege.getDeniedFields(), indicesPrivilege.getQuery());
} else {
value.merge(new MergableIndicesPrivilege(indicesPrivilege.getIndices(), indicesPrivilege.getPrivileges(),
indicesPrivilege.getGrantedFields(), indicesPrivilege.getDeniedFields(), indicesPrivilege.getQuery()));
return value;
}
});
}
}
}
final Set<String> clusterPrivs = clusterPrivileges.isEmpty() ? null : clusterPrivileges;
final Privilege runAsPrivilege = runAs.isEmpty() ? Privilege.NONE : new Privilege(runAs, runAs.toArray(Strings.EMPTY_ARRAY));
Role.Builder builder = Role.builder(nameBuilder.toString(), fieldPermissionsCache)
.cluster(ClusterPrivilege.get(clusterPrivs))
.runAs(runAsPrivilege);
indicesPrivilegesMap.entrySet().forEach((entry) -> {
MergableIndicesPrivilege privilege = entry.getValue();
builder.add(fieldPermissionsCache.getFieldPermissions(privilege.grantedFields, privilege.deniedFields), privilege.query,
IndexPrivilege.get(privilege.privileges), privilege.indices.toArray(Strings.EMPTY_ARRAY));
});
return builder.build();
}
public void invalidateAll() {
numInvalidation.incrementAndGet();
negativeLookupCache.clear();
try (ReleasableLock ignored = readLock.acquire()) {
roleCache.invalidateAll();
}
}
public void invalidate(String role) {
numInvalidation.incrementAndGet();
// the cache cannot be modified while doing this operation per the terms of the cache iterator
try (ReleasableLock ignored = writeLock.acquire()) {
Iterator<Set<String>> keyIter = roleCache.keys().iterator();
while (keyIter.hasNext()) {
Set<String> key = keyIter.next();
if (key.contains(role)) {
keyIter.remove();
}
}
}
negativeLookupCache.remove(role);
}
public Map<String, Object> usageStats() {
Map<String, Object> usage = new HashMap<>(2);
@ -64,4 +236,49 @@ public class CompositeRolesStore extends AbstractComponent {
usage.put("native", nativeRolesStore.usageStats());
return usage;
}
/**
* A mutable class that can be used to represent the combination of one or more {@link IndicesPrivileges}
*/
private static class MergableIndicesPrivilege {
private Set<String> indices;
private Set<String> privileges;
private Set<String> grantedFields = null;
private Set<String> deniedFields = null;
private Set<BytesReference> query = null;
MergableIndicesPrivilege(String[] indices, String[] privileges, @Nullable String[] grantedFields, @Nullable String[] deniedFields,
@Nullable BytesReference query) {
this.indices = Sets.newHashSet(Objects.requireNonNull(indices));
this.privileges = Sets.newHashSet(Objects.requireNonNull(privileges));
this.grantedFields = grantedFields == null ? null : Sets.newHashSet(grantedFields);
this.deniedFields = deniedFields == null ? null : Sets.newHashSet(deniedFields);
if (query != null) {
this.query = Sets.newHashSet(query);
}
}
void merge(MergableIndicesPrivilege other) {
assert indices.equals(other.indices) : "index names must be equivalent in order to merge";
this.grantedFields = combineFieldSets(this.grantedFields, other.grantedFields);
this.deniedFields = combineFieldSets(this.deniedFields, other.deniedFields);
this.privileges.addAll(other.privileges);
if (this.query == null || other.query == null) {
this.query = null;
} else {
this.query.addAll(other.query);
}
}
private static Set<String> combineFieldSets(Set<String> set, Set<String> other) {
if (set == null || other == null) {
// null = grant all so it trumps others
return null;
} else {
set.addAll(other);
return set;
}
}
}
}

View File

@ -8,10 +8,9 @@ package org.elasticsearch.xpack.security.authz.store;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.message.ParameterizedMessage;
import org.apache.logging.log4j.util.Supplier;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.ElasticsearchParseException;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.component.AbstractLifecycleComponent;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContentParser;
@ -23,8 +22,7 @@ import org.elasticsearch.watcher.ResourceWatcherService;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.xpack.XPackSettings;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.IndicesPermission.Group;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.RoleDescriptor.IndicesPrivileges;
import org.elasticsearch.xpack.security.support.NoOpLogger;
import org.elasticsearch.xpack.security.support.Validation;
@ -34,8 +32,10 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.regex.Pattern;
@ -43,51 +43,41 @@ import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableMap;
public class FileRolesStore extends AbstractLifecycleComponent {
public class FileRolesStore extends AbstractComponent {
private static final Pattern IN_SEGMENT_LINE = Pattern.compile("^\\s+.+");
private static final Pattern SKIP_LINE = Pattern.compile("(^#.*|^\\s*)");
private final Path file;
private final Runnable listener;
private final ResourceWatcherService watcherService;
private final List<Runnable> listeners = new ArrayList<>();
private volatile Map<String, Role> permissions;
private volatile Map<String, RoleDescriptor> permissions;
public FileRolesStore(Settings settings, Environment env, ResourceWatcherService watcherService) {
this(settings, env, watcherService, () -> {});
public FileRolesStore(Settings settings, Environment env, ResourceWatcherService watcherService) throws IOException {
this(settings, env, watcherService, null);
}
public FileRolesStore(Settings settings, Environment env, ResourceWatcherService watcherService, Runnable listener) {
FileRolesStore(Settings settings, Environment env, ResourceWatcherService watcherService, Runnable listener) throws IOException {
super(settings);
this.file = resolveFile(env);
this.listener = listener;
this.watcherService = watcherService;
permissions = emptyMap();
}
@Override
protected void doStart() throws ElasticsearchException {
if (listener != null) {
listeners.add(listener);
}
FileWatcher watcher = new FileWatcher(file.getParent());
watcher.addListener(new FileListener());
try {
watcherService.add(watcher, ResourceWatcherService.Frequency.HIGH);
} catch (IOException e) {
throw new ElasticsearchException("failed to setup roles file watcher", e);
}
watcherService.add(watcher, ResourceWatcherService.Frequency.HIGH);
permissions = parseFile(file, logger, settings);
}
@Override
protected void doStop() throws ElasticsearchException {
}
@Override
protected void doClose() throws ElasticsearchException {
}
public Role role(String role) {
return permissions.get(role);
Set<RoleDescriptor> roleDescriptors(Set<String> roleNames) {
Set<RoleDescriptor> descriptors = new HashSet<>();
roleNames.forEach((name) -> {
RoleDescriptor descriptor = permissions.get(name);
if (descriptor != null) {
descriptors.add(descriptor);
}
});
return descriptors;
}
public Map<String, Object> usageStats() {
@ -96,10 +86,10 @@ public class FileRolesStore extends AbstractLifecycleComponent {
boolean dls = false;
boolean fls = false;
for (Role role : permissions.values()) {
for (Group group : role.indices()) {
fls = fls || group.getFieldPermissions().hasFieldLevelSecurity();
dls = dls || group.hasQuery();
for (RoleDescriptor descriptor : permissions.values()) {
for (IndicesPrivileges indicesPrivileges : descriptor.getIndicesPrivileges()) {
fls = fls || indicesPrivileges.getGrantedFields() != null || indicesPrivileges.getDeniedFields() != null;
dls = dls || indicesPrivileges.getQuery() != null;
}
if (fls && dls) {
break;
@ -111,40 +101,48 @@ public class FileRolesStore extends AbstractLifecycleComponent {
return usageStats;
}
void addListener(Runnable runnable) {
Objects.requireNonNull(runnable);
synchronized (this) {
listeners.add(runnable);
}
}
public static Path resolveFile(Environment env) {
return XPackPlugin.resolveConfigFile(env, "roles.yml");
}
public static Set<String> parseFileForRoleNames(Path path, Logger logger) {
Map<String, Role> roleMap = parseFile(path, logger, false, Settings.EMPTY);
Map<String, RoleDescriptor> roleMap = parseFile(path, logger, false, Settings.EMPTY);
if (roleMap == null) {
return emptySet();
}
return roleMap.keySet();
}
public static Map<String, Role> parseFile(Path path, Logger logger, Settings settings) {
public static Map<String, RoleDescriptor> parseFile(Path path, Logger logger, Settings settings) {
return parseFile(path, logger, true, settings);
}
public static Map<String, Role> parseFile(Path path, Logger logger, boolean resolvePermission, Settings settings) {
public static Map<String, RoleDescriptor> parseFile(Path path, Logger logger, boolean resolvePermission,
Settings settings) {
if (logger == null) {
logger = NoOpLogger.INSTANCE;
}
Map<String, Role> roles = new HashMap<>();
Map<String, RoleDescriptor> roles = new HashMap<>();
logger.debug("attempting to read roles file located at [{}]", path.toAbsolutePath());
if (Files.exists(path)) {
try {
List<String> roleSegments = roleSegments(path);
for (String segment : roleSegments) {
Role role = parseRole(segment, path, logger, resolvePermission, settings);
if (role != null) {
if (ReservedRolesStore.isReserved(role.name())) {
RoleDescriptor descriptor = parseRoleDescriptor(segment, path, logger, resolvePermission, settings);
if (descriptor != null) {
if (ReservedRolesStore.isReserved(descriptor.getName())) {
logger.warn("role [{}] is reserved. the relevant role definition in the mapping file will be ignored",
role.name());
descriptor.getName());
} else {
roles.put(role.name(), role);
roles.put(descriptor.getName(), descriptor);
}
}
}
@ -195,29 +193,6 @@ public class FileRolesStore extends AbstractLifecycleComponent {
return unmodifiableMap(roles);
}
@Nullable
private static Role parseRole(String segment, Path path, Logger logger, boolean resolvePermissions, Settings settings) {
RoleDescriptor descriptor = parseRoleDescriptor(segment, path, logger, resolvePermissions, settings);
if (descriptor != null) {
String roleName = descriptor.getName();
// first check if FLS/DLS is enabled on the role...
for (RoleDescriptor.IndicesPrivileges privilege : descriptor.getIndicesPrivileges()) {
if ((privilege.getQuery() != null || privilege.getFieldPermissions().hasFieldLevelSecurity())
&& XPackSettings.DLS_FLS_ENABLED.get(settings) == false) {
logger.error("invalid role definition [{}] in roles file [{}]. document and field level security is not " +
"enabled. set [{}] to [true] in the configuration file. skipping role...", roleName, path
.toAbsolutePath(), XPackSettings.DLS_FLS_ENABLED.getKey());
return null;
}
}
return Role.builder(descriptor).build();
} else {
return null;
}
}
@Nullable
static RoleDescriptor parseRoleDescriptor(String segment, Path path, Logger logger,
boolean resolvePermissions, Settings settings) {
@ -246,7 +221,7 @@ public class FileRolesStore extends AbstractLifecycleComponent {
// we pass true as last parameter because we do not want to reject files if field permissions
// are given in 2.x syntax
RoleDescriptor descriptor = RoleDescriptor.parse(roleName, parser, true);
return descriptor;
return checkDescriptor(descriptor, path, logger, settings);
} else {
logger.error("invalid role definition [{}] in roles file [{}]. skipping role...", roleName, path.toAbsolutePath());
return null;
@ -282,6 +257,22 @@ public class FileRolesStore extends AbstractLifecycleComponent {
return null;
}
@Nullable
private static RoleDescriptor checkDescriptor(RoleDescriptor descriptor, Path path, Logger logger, Settings settings) {
String roleName = descriptor.getName();
// first check if FLS/DLS is enabled on the role...
for (RoleDescriptor.IndicesPrivileges privilege : descriptor.getIndicesPrivileges()) {
if ((privilege.getQuery() != null || privilege.getGrantedFields() != null || privilege.getDeniedFields() != null)
&& XPackSettings.DLS_FLS_ENABLED.get(settings) == false) {
logger.error("invalid role definition [{}] in roles file [{}]. document and field level security is not " +
"enabled. set [{}] to [true] in the configuration file. skipping role...", roleName, path
.toAbsolutePath(), XPackSettings.DLS_FLS_ENABLED.getKey());
return null;
}
}
return descriptor;
}
private static List<String> roleSegments(Path path) throws IOException {
List<String> segments = new ArrayList<>();
StringBuilder builder = null;
@ -329,7 +320,10 @@ public class FileRolesStore extends AbstractLifecycleComponent {
"could not reload roles file [{}]. Current roles remain unmodified", file.toAbsolutePath()), e);
return;
}
listener.run();
synchronized (FileRolesStore.this) {
listeners.forEach(Runnable::run);
}
}
}
}

View File

@ -20,7 +20,6 @@ import org.elasticsearch.action.search.MultiSearchRequestBuilder;
import org.elasticsearch.action.search.MultiSearchResponse;
import org.elasticsearch.action.search.MultiSearchResponse.Item;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.support.ThreadedActionListener;
import org.elasticsearch.action.support.TransportActions;
import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterState;
@ -28,21 +27,17 @@ import org.elasticsearch.cluster.ClusterStateListener;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.cache.Cache;
import org.elasticsearch.common.cache.CacheBuilder;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Setting.Property;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.concurrent.ReleasableLock;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.gateway.GatewayService;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.get.GetResult;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.xpack.security.InternalClient;
import org.elasticsearch.xpack.security.SecurityTemplateService;
import org.elasticsearch.xpack.security.action.role.ClearRolesCacheRequest;
@ -50,8 +45,6 @@ import org.elasticsearch.xpack.security.action.role.ClearRolesCacheResponse;
import org.elasticsearch.xpack.security.action.role.DeleteRoleRequest;
import org.elasticsearch.xpack.security.action.role.PutRoleRequest;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.IndicesPermission.Group;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.client.SecurityClient;
import java.util.Arrays;
@ -61,11 +54,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
@ -92,33 +81,19 @@ public class NativeRolesStore extends AbstractComponent implements ClusterStateL
FAILED
}
// these are no longer used, but leave them around for users upgrading
private static final Setting<Integer> CACHE_SIZE_SETTING =
Setting.intSetting(setting("authz.store.roles.index.cache.max_size"), 10000, Property.NodeScope);
private static final Setting<TimeValue> CACHE_TTL_SETTING =
Setting.timeSetting(setting("authz.store.roles.index.cache.ttl"), TimeValue.timeValueMinutes(20), Property.NodeScope);
Setting.intSetting(setting("authz.store.roles.index.cache.max_size"), 10000, Property.NodeScope, Property.Deprecated);
private static final Setting<TimeValue> CACHE_TTL_SETTING = Setting.timeSetting(setting("authz.store.roles.index.cache.ttl"),
TimeValue.timeValueMinutes(20), Property.NodeScope, Property.Deprecated);
private static final String ROLE_DOC_TYPE = "role";
private final InternalClient client;
private final AtomicReference<State> state = new AtomicReference<>(State.INITIALIZED);
private final boolean isTribeNode;
private final Cache<String, RoleAndVersion> roleCache;
// the lock is used in an odd manner; when iterating over the cache we cannot have modifiers other than deletes using
// the iterator but when not iterating we can modify the cache without external locking. When making normal modifications to the cache
// the read lock is obtained so that we can allow concurrent modifications; however when we need to iterate over the keys or values of
// the cache the write lock must obtained to prevent any modifications
private final ReleasableLock readLock;
private final ReleasableLock writeLock;
{
final ReadWriteLock iterationLock = new ReentrantReadWriteLock();
readLock = new ReleasableLock(iterationLock.readLock());
writeLock = new ReleasableLock(iterationLock.writeLock());
}
private SecurityClient securityClient;
// incremented each time the cache is invalidated
private final AtomicLong numInvalidation = new AtomicLong(0);
private volatile boolean securityIndexExists = false;
private volatile boolean canWrite = false;
@ -126,10 +101,6 @@ public class NativeRolesStore extends AbstractComponent implements ClusterStateL
public NativeRolesStore(Settings settings, InternalClient client) {
super(settings);
this.client = client;
this.roleCache = CacheBuilder.<String, RoleAndVersion>builder()
.setMaximumWeight(CACHE_SIZE_SETTING.get(settings))
.setExpireAfterWrite(CACHE_TTL_SETTING.get(settings))
.build();
this.isTribeNode = settings.getGroups("tribe", true).isEmpty() == false;
}
@ -198,13 +169,13 @@ public class NativeRolesStore extends AbstractComponent implements ClusterStateL
public void getRoleDescriptors(String[] names, final ActionListener<Collection<RoleDescriptor>> listener) {
if (state() != State.STARTED) {
logger.trace("attempted to get roles before service was started");
listener.onFailure(new IllegalStateException("roles cannot be retrieved as native role service has not been started"));
listener.onResponse(Collections.emptySet());
return;
}
if (names != null && names.length == 1) {
getRoleAndVersion(Objects.requireNonNull(names[0]), ActionListener.wrap(roleAndVersion ->
listener.onResponse(roleAndVersion == null || roleAndVersion.getRoleDescriptor() == null ? Collections.emptyList()
: Collections.singletonList(roleAndVersion.getRoleDescriptor())), listener::onFailure));
getRoleDescriptor(Objects.requireNonNull(names[0]), ActionListener.wrap(roleDescriptor ->
listener.onResponse(roleDescriptor == null ? Collections.emptyList() : Collections.singletonList(roleDescriptor)),
listener::onFailure));
} else {
try {
QueryBuilder query;
@ -303,25 +274,6 @@ public class NativeRolesStore extends AbstractComponent implements ClusterStateL
}
}
public void role(String roleName, ActionListener<Role> listener) {
if (state() != State.STARTED) {
listener.onResponse(null);
} else {
getRoleAndVersion(roleName, new ActionListener<RoleAndVersion>() {
@Override
public void onResponse(RoleAndVersion roleAndVersion) {
listener.onResponse(roleAndVersion == null ? null : roleAndVersion.getRole());
}
@Override
public void onFailure(Exception e) {
listener.onFailure(e);
}
});
}
}
public Map<String, Object> usageStats() {
if (state() != State.STARTED) {
return Collections.emptyMap();
@ -337,23 +289,9 @@ public class NativeRolesStore extends AbstractComponent implements ClusterStateL
return usageStats;
}
// FIXME this needs to be async
long count = 0L;
try (final ReleasableLock ignored = writeLock.acquire()) {
for (RoleAndVersion rv : roleCache.values()) {
if (rv == RoleAndVersion.NON_EXISTENT) {
continue;
}
count++;
Role role = rv.getRole();
for (Group group : role.indices()) {
fls = fls || group.getFieldPermissions().hasFieldLevelSecurity();
dls = dls || group.hasQuery();
}
}
}
// slow path - query for necessary information
// query for necessary information
if (fls == false || dls == false) {
MultiSearchRequestBuilder builder = client.prepareMultiSearch()
.add(client.prepareSearch(SecurityTemplateService.SECURITY_INDEX_NAME)
@ -407,67 +345,37 @@ public class NativeRolesStore extends AbstractComponent implements ClusterStateL
return usageStats;
}
private void getRoleAndVersion(final String roleId, ActionListener<RoleAndVersion> roleActionListener) {
private void getRoleDescriptor(final String roleId, ActionListener<RoleDescriptor> roleActionListener) {
if (securityIndexExists == false) {
roleActionListener.onResponse(null);
} else {
RoleAndVersion cachedRoleAndVersion = roleCache.get(roleId);
if (cachedRoleAndVersion == null) {
final long invalidationCounter = numInvalidation.get();
executeGetRoleRequest(roleId, new ActionListener<GetResponse>() {
@Override
public void onResponse(GetResponse response) {
final RoleAndVersion roleAndVersion;
RoleDescriptor descriptor = transformRole(response);
if (descriptor != null) {
logger.debug("loaded role [{}] from index with version [{}]", roleId, response.getVersion());
roleAndVersion = new RoleAndVersion(descriptor, response.getVersion());
} else {
roleAndVersion = RoleAndVersion.NON_EXISTENT;
}
executeGetRoleRequest(roleId, new ActionListener<GetResponse>() {
@Override
public void onResponse(GetResponse response) {
final RoleDescriptor descriptor = transformRole(response);
roleActionListener.onResponse(descriptor);
}
/* this is kinda spooky. We use a read/write lock to ensure we don't modify the cache if we hold the write
* lock (fetching stats for instance - which is kinda overkill?) but since we fetching stuff in an async
* fashion we need to make sure that if the cacht got invalidated since we started the request we don't
* put a potential stale result in the cache, hence the numInvalidation.get() comparison to the number of
* invalidation when we started. we just try to be on the safe side and don't cache potentially stale
* results*/
try (final ReleasableLock ignored = readLock.acquire()) {
if (invalidationCounter == numInvalidation.get()) {
roleCache.computeIfAbsent(roleId, (k) -> roleAndVersion);
}
} catch (ExecutionException e) {
throw new AssertionError("failed to load constant non-null value", e);
}
roleActionListener.onResponse(roleAndVersion);
@Override
public void onFailure(Exception e) {
// if the index or the shard is not there / available we just claim the role is not there
if (TransportActions.isShardNotAvailableException(e)) {
logger.warn((Supplier<?>) () -> new ParameterizedMessage("failed to load role [{}] index not available",
roleId), e);
roleActionListener.onResponse(null);
} else {
logger.error((Supplier<?>) () -> new ParameterizedMessage("failed to load role [{}]", roleId), e);
roleActionListener.onFailure(e);
}
@Override
public void onFailure(Exception e) {
// if the index or the shard is not there / available we just claim the role is not there
if (TransportActions.isShardNotAvailableException(e)) {
logger.warn((Supplier<?>) () -> new ParameterizedMessage("failed to load role [{}] index not available",
roleId), e);
roleActionListener.onResponse(RoleAndVersion.NON_EXISTENT);
} else {
logger.error((Supplier<?>) () -> new ParameterizedMessage("failed to load role [{}]", roleId), e);
roleActionListener.onFailure(e);
}
}
});
} else {
roleActionListener.onResponse(cachedRoleAndVersion);
}
}
});
}
}
// pkg-private for testing
void executeGetRoleRequest(String role, ActionListener<GetResponse> listener) {
private void executeGetRoleRequest(String role, ActionListener<GetResponse> listener) {
try {
GetRequest request = client.prepareGet(SecurityTemplateService.SECURITY_INDEX_NAME, ROLE_DOC_TYPE, role).request();
// TODO we use a threaded listener here to make sure we don't execute on a transport thread. This can be removed once
// all blocking operations are removed from this and NativeUserStore
client.get(request, new ThreadedActionListener<>(logger, client.threadPool(), ThreadPool.Names.LISTENER, listener, true));
client.get(request, listener);
} catch (IndexNotFoundException e) {
logger.trace(
(Supplier<?>) () -> new ParameterizedMessage(
@ -487,28 +395,11 @@ public class NativeRolesStore extends AbstractComponent implements ClusterStateL
if (state != State.STOPPED && state != State.FAILED) {
throw new IllegalStateException("can only reset if stopped!!!");
}
invalidateAll();
this.securityIndexExists = false;
this.canWrite = false;
this.state.set(State.INITIALIZED);
}
public void invalidateAll() {
logger.debug("invalidating all roles in cache");
numInvalidation.incrementAndGet();
try (final ReleasableLock ignored = readLock.acquire()) {
roleCache.invalidateAll();
}
}
public void invalidate(String role) {
logger.debug("invalidating role [{}] in cache", role);
numInvalidation.incrementAndGet();
try (final ReleasableLock ignored = readLock.acquire()) {
roleCache.invalidate(role);
}
}
private <Response> void clearRoleCache(final String role, ActionListener<Response> listener, Response response) {
ClearRolesCacheRequest request = new ClearRolesCacheRequest().names(role);
securityClient.clearRolesCache(request, new ActionListener<ClearRolesCacheResponse>() {
@ -558,39 +449,6 @@ public class NativeRolesStore extends AbstractComponent implements ClusterStateL
}
}
private static class RoleAndVersion {
private static final RoleAndVersion NON_EXISTENT = new RoleAndVersion();
private final RoleDescriptor roleDescriptor;
private final Role role;
private final long version;
private RoleAndVersion() {
roleDescriptor = null;
role = null;
version = Long.MIN_VALUE;
}
RoleAndVersion(RoleDescriptor roleDescriptor, long version) {
this.roleDescriptor = roleDescriptor;
this.role = Role.builder(roleDescriptor).build();
this.version = version;
}
RoleDescriptor getRoleDescriptor() {
return roleDescriptor;
}
Role getRole() {
return role;
}
long getVersion() {
return version;
}
}
public static void addSettings(List<Setting<?>> settings) {
settings.add(CACHE_SIZE_SETTING);
settings.add(CACHE_TTL_SETTING);

View File

@ -5,54 +5,61 @@
*/
package org.elasticsearch.xpack.security.authz.store;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.xpack.monitoring.action.MonitoringBulkAction;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.IngestAdminRole;
import org.elasticsearch.xpack.security.authz.permission.KibanaRole;
import org.elasticsearch.xpack.security.authz.permission.KibanaUserRole;
import org.elasticsearch.xpack.security.authz.permission.LogstashSystemRole;
import org.elasticsearch.xpack.security.authz.permission.MonitoringUserRole;
import org.elasticsearch.xpack.security.authz.permission.RemoteMonitoringAgentRole;
import org.elasticsearch.xpack.security.authz.permission.ReportingUserRole;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.permission.SuperuserRole;
import org.elasticsearch.xpack.security.authz.permission.TransportClientRole;
import org.elasticsearch.xpack.security.support.MetadataUtils;
import org.elasticsearch.xpack.security.user.SystemUser;
public class ReservedRolesStore {
public ReservedRolesStore() {
}
public static final RoleDescriptor SUPERUSER_ROLE_DESCRIPTOR = new RoleDescriptor("superuser", new String[] { "all" },
new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices("*").privileges("all").build()},
new String[] { "*" },
MetadataUtils.DEFAULT_RESERVED_METADATA);
public static final Role SUPERUSER_ROLE = Role.builder(SUPERUSER_ROLE_DESCRIPTOR, null).build();
private static final Map<String, RoleDescriptor> RESERVED_ROLES = initializeReservedRoles();
public Role role(String role) {
switch (role) {
case SuperuserRole.NAME:
return SuperuserRole.INSTANCE;
case TransportClientRole.NAME:
return TransportClientRole.INSTANCE;
case KibanaUserRole.NAME:
return KibanaUserRole.INSTANCE;
case MonitoringUserRole.NAME:
return MonitoringUserRole.INSTANCE;
case RemoteMonitoringAgentRole.NAME:
return RemoteMonitoringAgentRole.INSTANCE;
case IngestAdminRole.NAME:
return IngestAdminRole.INSTANCE;
case ReportingUserRole.NAME:
return ReportingUserRole.INSTANCE;
case KibanaRole.NAME:
return KibanaRole.INSTANCE;
case LogstashSystemRole.NAME:
return LogstashSystemRole.INSTANCE;
default:
return null;
}
private static Map<String, RoleDescriptor> initializeReservedRoles() {
return MapBuilder.<String, RoleDescriptor>newMapBuilder()
.put("superuser", new RoleDescriptor("superuser", new String[] { "all" },
new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices("*").privileges("all").build()},
new String[] { "*" },
MetadataUtils.DEFAULT_RESERVED_METADATA))
.put("transport_client", new RoleDescriptor("transport_client", new String[] { "transport_client" }, null, null,
MetadataUtils.DEFAULT_RESERVED_METADATA))
.put("kibana_user", new RoleDescriptor("kibana_user", new String[] { "monitor" }, new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices(".kibana*").privileges("manage", "read", "index", "delete")
.build() }, null, MetadataUtils.DEFAULT_RESERVED_METADATA))
.put("monitoring_user", new RoleDescriptor("monitoring_user", null, new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices(".marvel-es-*", ".monitoring-*").privileges("read").build() },
null, MetadataUtils.DEFAULT_RESERVED_METADATA))
.put("remote_monitoring_agent", new RoleDescriptor("remote_monitoring_agent",
new String[] { "manage_index_templates", "manage_ingest_pipelines", "monitor" },
new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices(".marvel-es-*", ".monitoring-*").privileges("all").build() },
null, MetadataUtils.DEFAULT_RESERVED_METADATA))
.put("ingest_admin", new RoleDescriptor("ingest_admin", new String[] { "manage_index_templates", "manage_pipeline" },
null, null, MetadataUtils.DEFAULT_RESERVED_METADATA))
.put("reporting_user", new RoleDescriptor("reporting_user", null, new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices(".reporting-*").privileges("read", "write").build() },
null, MetadataUtils.DEFAULT_RESERVED_METADATA))
.put("kibana", new RoleDescriptor("kibana", new String[] { "monitor", MonitoringBulkAction.NAME},
new RoleDescriptor.IndicesPrivileges[] {
RoleDescriptor.IndicesPrivileges.builder().indices(".kibana*", ".reporting-*").privileges("all").build() },
null, MetadataUtils.DEFAULT_RESERVED_METADATA))
.put("logstash_system", new RoleDescriptor("logstash_system", new String[] { "monitor", MonitoringBulkAction.NAME},
null, null, MetadataUtils.DEFAULT_RESERVED_METADATA))
.immutableMap();
}
public Map<String, Object> usageStats() {
@ -60,58 +67,19 @@ public class ReservedRolesStore {
}
public RoleDescriptor roleDescriptor(String role) {
switch (role) {
case SuperuserRole.NAME:
return SuperuserRole.DESCRIPTOR;
case TransportClientRole.NAME:
return TransportClientRole.DESCRIPTOR;
case KibanaUserRole.NAME:
return KibanaUserRole.DESCRIPTOR;
case MonitoringUserRole.NAME:
return MonitoringUserRole.DESCRIPTOR;
case RemoteMonitoringAgentRole.NAME:
return RemoteMonitoringAgentRole.DESCRIPTOR;
case IngestAdminRole.NAME:
return IngestAdminRole.DESCRIPTOR;
case ReportingUserRole.NAME:
return ReportingUserRole.DESCRIPTOR;
case KibanaRole.NAME:
return KibanaRole.DESCRIPTOR;
case LogstashSystemRole.NAME:
return LogstashSystemRole.DESCRIPTOR;
default:
return null;
}
return RESERVED_ROLES.get(role);
}
public Collection<RoleDescriptor> roleDescriptors() {
return Arrays.asList(SuperuserRole.DESCRIPTOR, TransportClientRole.DESCRIPTOR, KibanaUserRole.DESCRIPTOR,
KibanaRole.DESCRIPTOR, MonitoringUserRole.DESCRIPTOR, RemoteMonitoringAgentRole.DESCRIPTOR,
IngestAdminRole.DESCRIPTOR, ReportingUserRole.DESCRIPTOR, LogstashSystemRole.DESCRIPTOR);
return RESERVED_ROLES.values();
}
public static Set<String> names() {
return Sets.newHashSet(SuperuserRole.NAME, KibanaRole.NAME, TransportClientRole.NAME, KibanaUserRole.NAME,
MonitoringUserRole.NAME, RemoteMonitoringAgentRole.NAME, IngestAdminRole.NAME, ReportingUserRole.NAME,
LogstashSystemRole.NAME);
return RESERVED_ROLES.keySet();
}
public static boolean isReserved(String role) {
switch (role) {
case SuperuserRole.NAME:
case KibanaRole.NAME:
case KibanaUserRole.NAME:
case TransportClientRole.NAME:
case MonitoringUserRole.NAME:
case RemoteMonitoringAgentRole.NAME:
case SystemUser.ROLE_NAME:
case IngestAdminRole.NAME:
case ReportingUserRole.NAME:
case LogstashSystemRole.NAME:
return true;
default:
return false;
}
return RESERVED_ROLES.containsKey(role) || SystemUser.ROLE_NAME.equals(role);
}
}

View File

@ -1,27 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.support;
import org.apache.lucene.util.automaton.Automaton;
import org.apache.lucene.util.automaton.CharacterRunAutomaton;
import java.util.function.Predicate;
import static org.apache.lucene.util.automaton.Operations.DEFAULT_MAX_DETERMINIZED_STATES;
public class AutomatonPredicate implements Predicate<String> {
private final CharacterRunAutomaton automaton;
public AutomatonPredicate(Automaton automaton) {
this.automaton = new CharacterRunAutomaton(automaton, DEFAULT_MAX_DETERMINIZED_STATES);
}
@Override
public boolean test(String input) {
return automaton.run(input);
}
}

View File

@ -7,17 +7,18 @@ package org.elasticsearch.xpack.security.support;
import org.apache.lucene.util.automaton.Automata;
import org.apache.lucene.util.automaton.Automaton;
import org.apache.lucene.util.automaton.CharacterRunAutomaton;
import org.apache.lucene.util.automaton.RegExp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.function.Predicate;
import static org.apache.lucene.util.automaton.MinimizationOperations.minimize;
import static org.apache.lucene.util.automaton.Operations.DEFAULT_MAX_DETERMINIZED_STATES;
import static org.apache.lucene.util.automaton.Operations.concatenate;
import static org.apache.lucene.util.automaton.Operations.determinize;
import static org.apache.lucene.util.automaton.Operations.minus;
import static org.apache.lucene.util.automaton.Operations.union;
@ -106,13 +107,26 @@ public final class Automatons {
return concatenate(automata);
}
public static Automaton unionAndDeterminize(Automaton a1, Automaton a2) {
Automaton res = union(a1, a2);
return determinize(res, DEFAULT_MAX_DETERMINIZED_STATES);
public static Automaton unionAndMinimize(Collection<Automaton> automata) {
Automaton res = union(automata);
return minimize(res, DEFAULT_MAX_DETERMINIZED_STATES);
}
public static Automaton minusAndDeterminize(Automaton a1, Automaton a2) {
public static Automaton minusAndMinimize(Automaton a1, Automaton a2) {
Automaton res = minus(a1, a2, DEFAULT_MAX_DETERMINIZED_STATES);
return determinize(res, DEFAULT_MAX_DETERMINIZED_STATES);
return minimize(res, DEFAULT_MAX_DETERMINIZED_STATES);
}
public static Predicate<String> predicate(String... patterns) {
return predicate(Arrays.asList(patterns));
}
public static Predicate<String> predicate(Collection<String> patterns) {
return predicate(patterns(patterns));
}
public static Predicate<String> predicate(Automaton automaton) {
CharacterRunAutomaton runAutomaton = new CharacterRunAutomaton(automaton, DEFAULT_MAX_DETERMINIZED_STATES);
return runAutomaton::run;
}
}

View File

@ -10,7 +10,6 @@ import io.netty.handler.ipfilter.IpFilterRuleType;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.SetOnce;
import org.elasticsearch.common.collect.MapBuilder;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.ClusterSettings;
import org.elasticsearch.common.settings.Setting;
@ -22,7 +21,6 @@ import org.elasticsearch.license.XPackLicenseState;
import org.elasticsearch.transport.TransportSettings;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;

View File

@ -5,7 +5,6 @@
*/
package org.elasticsearch.xpack.security.user;
import org.elasticsearch.xpack.security.authz.permission.SuperuserRole;
import org.elasticsearch.xpack.security.support.MetadataUtils;
/**
@ -15,7 +14,7 @@ import org.elasticsearch.xpack.security.support.MetadataUtils;
public class ElasticUser extends User {
public static final String NAME = "elastic";
public static final String ROLE_NAME = SuperuserRole.NAME;
private static final String ROLE_NAME = "superuser";
public ElasticUser(boolean enabled) {
super(NAME, new String[] { ROLE_NAME }, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, enabled);

View File

@ -5,7 +5,6 @@
*/
package org.elasticsearch.xpack.security.user;
import org.elasticsearch.xpack.security.authz.permission.KibanaRole;
import org.elasticsearch.xpack.security.support.MetadataUtils;
/**
@ -14,7 +13,7 @@ import org.elasticsearch.xpack.security.support.MetadataUtils;
public class KibanaUser extends User {
public static final String NAME = "kibana";
public static final String ROLE_NAME = KibanaRole.NAME;
public static final String ROLE_NAME = "kibana";
public KibanaUser(boolean enabled) {
super(NAME, new String[]{ ROLE_NAME }, null, null, MetadataUtils.DEFAULT_RESERVED_METADATA, enabled);

View File

@ -6,8 +6,6 @@
package org.elasticsearch.xpack.security.user;
import org.elasticsearch.Version;
import org.elasticsearch.xpack.security.authz.permission.KibanaRole;
import org.elasticsearch.xpack.security.authz.permission.LogstashSystemRole;
import org.elasticsearch.xpack.security.support.MetadataUtils;
/**
@ -16,7 +14,7 @@ import org.elasticsearch.xpack.security.support.MetadataUtils;
public class LogstashSystemUser extends User {
public static final String NAME = "logstash_system";
public static final String ROLE_NAME = LogstashSystemRole.NAME;
private static final String ROLE_NAME = "logstash_system";
public static final Version DEFINED_SINCE = Version.V_5_2_0_UNRELEASED;
public LogstashSystemUser(boolean enabled) {

View File

@ -5,15 +5,13 @@
*/
package org.elasticsearch.xpack.security.user;
import org.elasticsearch.xpack.security.authz.permission.SuperuserRole;
/**
* XPack internal user that manages xpack. Has all cluster/indices permissions for x-pack to operate.
*/
public class XPackUser extends User {
public static final String NAME = "_xpack";
public static final String ROLE_NAME = SuperuserRole.NAME;
private static final String ROLE_NAME = "superuser";
public static final XPackUser INSTANCE = new XPackUser();
private XPackUser() {

View File

@ -109,19 +109,19 @@ public class OldSecurityIndexBackwardsCompatibilityTests extends AbstractOldXPac
RoleDescriptor.IndicesPrivileges indicesPrivileges = role.getIndicesPrivileges()[0];
assertThat(indicesPrivileges.getIndices(), arrayWithSize(2));
assertArrayEquals(new String[] { "index1", "index2" }, indicesPrivileges.getIndices());
assertTrue(indicesPrivileges.getFieldPermissions().grantsAccessTo("title"));
assertTrue(indicesPrivileges.getFieldPermissions().grantsAccessTo("body"));
final FieldPermissions fieldPermissions =
new FieldPermissions(indicesPrivileges.getGrantedFields(), indicesPrivileges.getDeniedFields());
assertTrue(fieldPermissions.grantsAccessTo("title"));
assertTrue(fieldPermissions.grantsAccessTo("body"));
assertArrayEquals(new String[] { "all" }, indicesPrivileges.getPrivileges());
assertEquals("{\"match\": {\"title\": \"foo\"}}", indicesPrivileges.getQuery().utf8ToString());
assertEquals("{\"match\": {\"title\": \"foo\"}}", indicesPrivileges.getQuery().iterator().next().utf8ToString());
assertArrayEquals(new String[] { "all" }, role.getClusterPrivileges());
assertArrayEquals(new String[] { "other_user" }, role.getRunAs());
assertEquals("bwc_test_role", role.getName());
// check x-content is rendered in new format although it comes from an old index
XContentBuilder builder = jsonBuilder();
builder.startObject();
indicesPrivileges.getFieldPermissions().toXContent(builder, null);
builder.endObject();
assertThat(builder.string(), equalTo("{\"field_security\":{\"grant\":[\"title\",\"body\"]}}"));
indicesPrivileges.toXContent(builder, null);
assertThat(builder.string(), containsString("\"field_security\":{\"grant\":[\"title\",\"body\"]}"));
logger.info("Getting users...");
assertBusy(() -> {
@ -167,7 +167,8 @@ public class OldSecurityIndexBackwardsCompatibilityTests extends AbstractOldXPac
PutRoleResponse roleResponse = securityClient.preparePutRole("test_role").addIndices(
new String[] { "index3" },
new String[] { "all" },
new FieldPermissions(new String[]{"title", "body"}, null),
new String[] { "title", "body" },
null,
new BytesArray("{\"term\": {\"title\":\"not\"}}")).cluster("all")
.get();
assertTrue(roleResponse.isCreated());

View File

@ -14,14 +14,14 @@ import org.elasticsearch.xpack.security.SecurityTemplateService;
import org.elasticsearch.xpack.security.action.role.DeleteRoleResponse;
import org.elasticsearch.xpack.security.action.role.GetRolesResponse;
import org.elasticsearch.xpack.security.action.role.PutRoleResponse;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.store.NativeRolesStore;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.junit.Before;
import org.junit.BeforeClass;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import static org.elasticsearch.action.support.WriteRequest.RefreshPolicy.IMMEDIATE;
@ -52,7 +52,7 @@ public class ClearRolesCacheTests extends NativeRealmIntegTestCase {
for (String role : roles) {
c.preparePutRole(role)
.cluster("none")
.addIndices(new String[] { "*" }, new String[] { "ALL" }, new FieldPermissions(), null)
.addIndices(new String[] { "*" }, new String[] { "ALL" }, null, null, null)
.get();
logger.debug("--> created role [{}]", role);
}
@ -61,11 +61,9 @@ public class ClearRolesCacheTests extends NativeRealmIntegTestCase {
// warm up the caches on every node
for (NativeRolesStore rolesStore : internalCluster().getInstances(NativeRolesStore.class)) {
for (String role : roles) {
PlainActionFuture<Role> future = new PlainActionFuture<>();
rolesStore.role(role, future);
assertThat(future.actionGet(), notNullValue());
}
PlainActionFuture<Collection<RoleDescriptor>> future = new PlainActionFuture<>();
rolesStore.getRoleDescriptors(roles, future);
assertThat(future.actionGet(), notNullValue());
}
}
@ -87,7 +85,7 @@ public class ClearRolesCacheTests extends NativeRealmIntegTestCase {
for (String role : toModify) {
PutRoleResponse response = securityClient.preparePutRole(role)
.cluster("none")
.addIndices(new String[] { "*" }, new String[] { "ALL" }, new FieldPermissions(), null)
.addIndices(new String[] { "*" }, new String[] { "ALL" }, null, null, null)
.runAs(role)
.setRefreshPolicy(randomBoolean() ? IMMEDIATE : NONE)
.get();

View File

@ -30,12 +30,12 @@ import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.crypto.CryptoService;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
import org.junit.Before;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@ -95,15 +95,16 @@ public class SecurityActionFilterTests extends ESTestCase {
callback.onResponse(authentication);
return Void.TYPE;
}).when(authcService).authenticate(eq("_action"), eq(request), eq(SystemUser.INSTANCE), any(ActionListener.class));
final Role empty = Role.EMPTY;
doAnswer((i) -> {
ActionListener callback =
(ActionListener) i.getArguments()[1];
callback.onResponse(Collections.emptyList());
callback.onResponse(empty);
return Void.TYPE;
}).when(authzService).roles(any(User.class), any(ActionListener.class));
doReturn(request).when(spy(filter)).unsign(user, "_action", request);
filter.apply(task, "_action", request, listener, chain);
verify(authzService).authorize(authentication, "_action", request, Collections.emptyList(), Collections.emptyList());
verify(authzService).authorize(authentication, "_action", request, empty, null);
verify(chain).proceed(eq(task), eq("_action"), eq(request), isA(ContextPreservingActionListener.class));
}
@ -123,10 +124,11 @@ public class SecurityActionFilterTests extends ESTestCase {
callback.onResponse(authentication);
return Void.TYPE;
}).when(authcService).authenticate(eq(action), eq(request), eq(SystemUser.INSTANCE), any(ActionListener.class));
final Role empty = Role.EMPTY;
doAnswer((i) -> {
ActionListener callback =
(ActionListener) i.getArguments()[1];
callback.onResponse(Collections.emptyList());
callback.onResponse(empty);
return Void.TYPE;
}).when(authzService).roles(any(User.class), any(ActionListener.class));
doReturn(request).when(spy(filter)).unsign(user, action, request);
@ -135,7 +137,7 @@ public class SecurityActionFilterTests extends ESTestCase {
verify(listener).onFailure(isA(IllegalArgumentException.class));
verifyNoMoreInteractions(authzService, chain);
} else {
verify(authzService).authorize(authentication, action, request, Collections.emptyList(), Collections.emptyList());
verify(authzService).authorize(authentication, action, request, empty, null);
verify(chain).proceed(eq(task), eq(action), eq(request), isA(ContextPreservingActionListener.class));
}
}
@ -157,11 +159,11 @@ public class SecurityActionFilterTests extends ESTestCase {
doAnswer((i) -> {
ActionListener callback =
(ActionListener) i.getArguments()[1];
callback.onResponse(Collections.emptyList());
callback.onResponse(Role.EMPTY);
return Void.TYPE;
}).when(authzService).roles(any(User.class), any(ActionListener.class));
doThrow(exception).when(authzService).authorize(eq(authentication), eq("_action"), eq(request), any(Collection.class),
any(Collection.class));
doThrow(exception).when(authzService).authorize(eq(authentication), eq("_action"), eq(request), any(Role.class),
any(Role.class));
filter.apply(task, "_action", request, listener, chain);
verify(listener).onFailure(exception);
verifyNoMoreInteractions(chain);
@ -182,16 +184,17 @@ public class SecurityActionFilterTests extends ESTestCase {
}).when(authcService).authenticate(eq("_action"), eq(request), eq(SystemUser.INSTANCE), any(ActionListener.class));
when(cryptoService.isSigned("signed_scroll_id")).thenReturn(true);
when(cryptoService.unsignAndVerify("signed_scroll_id")).thenReturn("scroll_id");
final Role empty = Role.EMPTY;
doAnswer((i) -> {
ActionListener callback =
(ActionListener) i.getArguments()[1];
callback.onResponse(Collections.emptyList());
callback.onResponse(empty);
return Void.TYPE;
}).when(authzService).roles(any(User.class), any(ActionListener.class));
filter.apply(task, "_action", request, listener, chain);
assertThat(request.scrollId(), equalTo("scroll_id"));
verify(authzService).authorize(authentication, "_action", request, Collections.emptyList(), Collections.emptyList());
verify(authzService).authorize(authentication, "_action", request, empty, null);
verify(chain).proceed(eq(task), eq("_action"), eq(request), isA(ContextPreservingActionListener.class));
}
@ -214,7 +217,7 @@ public class SecurityActionFilterTests extends ESTestCase {
doAnswer((i) -> {
ActionListener callback =
(ActionListener) i.getArguments()[1];
callback.onResponse(Collections.emptyList());
callback.onResponse(Role.EMPTY);
return Void.TYPE;
}).when(authzService).roles(any(User.class), any(ActionListener.class));
filter.apply(task, "_action", request, listener, chain);

View File

@ -11,16 +11,9 @@ import org.elasticsearch.action.support.ActionFilters;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.xpack.security.SecurityContext;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.KibanaRole;
import org.elasticsearch.xpack.security.authz.permission.LogstashSystemRole;
import org.elasticsearch.xpack.security.authz.store.NativeRolesStore;
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
import org.elasticsearch.xpack.security.user.ElasticUser;
import org.elasticsearch.xpack.security.user.KibanaUser;
import org.elasticsearch.xpack.security.user.LogstashSystemUser;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
@ -33,33 +26,26 @@ import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.emptyArray;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.hamcrest.Matchers.nullValue;
import static org.mockito.AdditionalMatchers.aryEq;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
public class TransportGetRolesActionTests extends ESTestCase {
public void testReservedRoles() {
NativeRolesStore rolesStore = mock(NativeRolesStore.class);
SecurityContext context = mock(SecurityContext.class);
TransportService transportService = new TransportService(Settings.EMPTY, null, null, TransportService.NOOP_TRANSPORT_INTERCEPTOR,
null);
TransportGetRolesAction action = new TransportGetRolesAction(Settings.EMPTY, mock(ThreadPool.class), mock(ActionFilters.class),
mock(IndexNameExpressionResolver.class), rolesStore, transportService, new ReservedRolesStore());
final User executingUser = randomFrom(new ElasticUser(true), new KibanaUser(true), new LogstashSystemUser(true));
when(context.getUser()).thenReturn(executingUser);
final int size = randomIntBetween(1, ReservedRolesStore.names().size());
final List<String> names = randomSubsetOf(size, ReservedRolesStore.names());
@ -101,15 +87,11 @@ public class TransportGetRolesActionTests extends ESTestCase {
public void testStoreRoles() {
final List<RoleDescriptor> storeRoleDescriptors = randomRoleDescriptors();
NativeRolesStore rolesStore = mock(NativeRolesStore.class);
SecurityContext context = mock(SecurityContext.class);
TransportService transportService = new TransportService(Settings.EMPTY, null, null, TransportService.NOOP_TRANSPORT_INTERCEPTOR,
null);
TransportGetRolesAction action = new TransportGetRolesAction(Settings.EMPTY, mock(ThreadPool.class), mock(ActionFilters.class),
mock(IndexNameExpressionResolver.class), rolesStore, transportService, new ReservedRolesStore());
final User executingUser = randomFrom(new ElasticUser(true), new KibanaUser(true), new LogstashSystemUser(true));
when(context.getUser()).thenReturn(executingUser);
GetRolesRequest request = new GetRolesRequest();
request.names(storeRoleDescriptors.stream().map(RoleDescriptor::getName).collect(Collectors.toList()).toArray(Strings.EMPTY_ARRAY));
@ -157,15 +139,11 @@ public class TransportGetRolesActionTests extends ESTestCase {
}
NativeRolesStore rolesStore = mock(NativeRolesStore.class);
SecurityContext context = mock(SecurityContext.class);
TransportService transportService = new TransportService(Settings.EMPTY, null, null, TransportService.NOOP_TRANSPORT_INTERCEPTOR,
null);
TransportGetRolesAction action = new TransportGetRolesAction(Settings.EMPTY, mock(ThreadPool.class), mock(ActionFilters.class),
mock(IndexNameExpressionResolver.class), rolesStore, transportService, new ReservedRolesStore());
final User executingUser = randomFrom(new ElasticUser(true), new KibanaUser(true), new LogstashSystemUser(true));
when(context.getUser()).thenReturn(executingUser);
final List<String> expectedNames = new ArrayList<>();
if (all) {
expectedNames.addAll(reservedRoleNames);
@ -225,7 +203,6 @@ public class TransportGetRolesActionTests extends ESTestCase {
final Exception e = randomFrom(new ElasticsearchSecurityException(""), new IllegalStateException());
final List<RoleDescriptor> storeRoleDescriptors = randomRoleDescriptors();
NativeRolesStore rolesStore = mock(NativeRolesStore.class);
SecurityContext context = mock(SecurityContext.class);
TransportService transportService = new TransportService(Settings.EMPTY, null, null, TransportService.NOOP_TRANSPORT_INTERCEPTOR,
null);
TransportGetRolesAction action = new TransportGetRolesAction(Settings.EMPTY, mock(ThreadPool.class), mock(ActionFilters.class),

View File

@ -15,7 +15,6 @@ import org.elasticsearch.env.Environment;
import org.elasticsearch.test.NativeRealmIntegTestCase;
import org.elasticsearch.test.SecuritySettingsSource;
import org.elasticsearch.xpack.security.SecurityTemplateService;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.junit.BeforeClass;
@ -112,7 +111,7 @@ public class ESNativeMigrateToolTests extends NativeRealmIntegTestCase {
.cluster("all", "none")
.runAs("root", "nobody")
.addIndices(new String[]{"index"}, new String[]{"read"},
new FieldPermissions(new String[]{"body", "title"}, null), new BytesArray("{\"query\": {\"match_all\": {}}}"))
new String[]{"body", "title"}, null, new BytesArray("{\"query\": {\"match_all\": {}}}"))
.get();
addedRoles.add(rname);
}

View File

@ -50,7 +50,7 @@ public class ESNativeRealmMigrateToolTests extends CommandTestCase {
RoleDescriptor.IndicesPrivileges ip = RoleDescriptor.IndicesPrivileges.builder()
.indices(new String[]{"i1", "i2", "i3"})
.privileges(new String[]{"all"})
.fieldPermissions(new FieldPermissions(new String[]{"body"}, null))
.grantedFields("body")
.build();
RoleDescriptor.IndicesPrivileges[] ips = new RoleDescriptor.IndicesPrivileges[1];
ips[0] = ip;

View File

@ -29,12 +29,9 @@ import org.elasticsearch.xpack.security.action.user.DeleteUserResponse;
import org.elasticsearch.xpack.security.action.user.GetUsersResponse;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
import org.elasticsearch.xpack.security.authz.permission.KibanaRole;
import org.elasticsearch.xpack.security.authz.permission.LogstashSystemRole;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.permission.SuperuserRole;
import org.elasticsearch.xpack.security.authz.store.NativeRolesStore;
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.security.user.ElasticUser;
@ -94,7 +91,7 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
PutRoleResponse response = securityClient()
.preparePutRole("native_anonymous")
.cluster("ALL")
.addIndices(new String[]{"*"}, new String[]{"ALL"}, new FieldPermissions(), null)
.addIndices(new String[]{"*"}, new String[]{"ALL"}, null, null, null)
.get();
assertTrue(response.isCreated());
} else {
@ -171,14 +168,13 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
SecurityClient c = securityClient();
final List<RoleDescriptor> existingRoles = Arrays.asList(c.prepareGetRoles().get().roles());
final int existing = existingRoles.size();
final Map<String, Object> metadata = Collections.singletonMap("key", (Object) randomAsciiOfLengthBetween(1, 10));
final Map<String, Object> metadata = Collections.singletonMap("key", randomAsciiOfLengthBetween(1, 10));
logger.error("--> creating role");
c.preparePutRole("test_role")
.cluster("all", "none")
.runAs("root", "nobody")
.addIndices(new String[]{"index"}, new String[]{"read"},
new FieldPermissions(new String[]{"body", "title"}, null), new BytesArray("{\"query\": " +
"{\"match_all\": {}}}"))
.addIndices(new String[]{"index"}, new String[]{"read"}, new String[]{"body", "title"}, null,
new BytesArray("{\"query\": {\"match_all\": {}}}"))
.metadata(metadata)
.get();
logger.error("--> waiting for .security index");
@ -194,16 +190,14 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
c.preparePutRole("test_role2")
.cluster("all", "none")
.runAs("root", "nobody")
.addIndices(new String[]{"index"}, new String[]{"read"},
new FieldPermissions(new String[]{"body", "title"}, null), new BytesArray("{\"query\": " +
"{\"match_all\": {}}}"))
.addIndices(new String[]{"index"}, new String[]{"read"}, new String[]{"body", "title"}, null,
new BytesArray("{\"query\": {\"match_all\": {}}}"))
.get();
c.preparePutRole("test_role3")
.cluster("all", "none")
.runAs("root", "nobody")
.addIndices(new String[]{"index"}, new String[]{"read"},
new FieldPermissions(new String[]{"body", "title"}, null), new BytesArray("{\"query\": " +
"{\"match_all\": {}}}"))
.addIndices(new String[]{"index"}, new String[]{"read"}, new String[]{"body", "title"}, null,
new BytesArray("{\"query\": {\"match_all\": {}}}"))
.get();
logger.info("--> retrieving all roles");
@ -229,8 +223,7 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
logger.error("--> creating role");
c.preparePutRole("test_role")
.cluster("all")
.addIndices(new String[] { "*" }, new String[] { "read" },
new FieldPermissions(new String[] { "body", "title" }, null),
.addIndices(new String[] { "*" }, new String[] { "read" }, new String[]{"body", "title"}, null,
new BytesArray("{\"match_all\": {}}"))
.get();
logger.error("--> creating user");
@ -324,8 +317,8 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
logger.error("--> creating role");
c.preparePutRole("test_role")
.cluster("all")
.addIndices(new String[]{"*"}, new String[]{"read"},
new FieldPermissions(new String[]{"body", "title"}, null), new BytesArray("{\"match_all\": {}}"))
.addIndices(new String[]{"*"}, new String[]{"read"}, new String[]{"body", "title"}, null,
new BytesArray("{\"match_all\": {}}"))
.get();
logger.error("--> creating user");
c.preparePutUser("joe", "s3krit".toCharArray(), "test_role").get();
@ -339,8 +332,8 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
assertFalse(response.isTimedOut());
c.preparePutRole("test_role")
.cluster("none")
.addIndices(new String[]{"*"}, new String[]{"read"},
new FieldPermissions(new String[]{"body", "title"}, null), new BytesArray("{\"match_all\": {}}"))
.addIndices(new String[]{"*"}, new String[]{"read"}, new String[]{"body", "title"}, null,
new BytesArray("{\"match_all\": {}}"))
.get();
if (anonymousEnabled && roleExists) {
assertNoTimeout(client()
@ -354,18 +347,18 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
GetRolesResponse getRolesResponse = c.prepareGetRoles().names("test_role").get();
assertTrue("test_role does not exist!", getRolesResponse.hasRoles());
assertTrue("any cluster permission should be authorized",
Role.builder(getRolesResponse.roles()[0]).build().cluster().check("cluster:admin/foo", null, null));
Role.builder(getRolesResponse.roles()[0], null).build().cluster().check("cluster:admin/foo"));
c.preparePutRole("test_role")
.cluster("none")
.addIndices(new String[]{"*"}, new String[]{"read"},
new FieldPermissions(new String[]{"body", "title"}, null), new BytesArray("{\"match_all\": {}}"))
.addIndices(new String[]{"*"}, new String[]{"read"}, new String[]{"body", "title"}, null,
new BytesArray("{\"match_all\": {}}"))
.get();
getRolesResponse = c.prepareGetRoles().names("test_role").get();
assertTrue("test_role does not exist!", getRolesResponse.hasRoles());
assertFalse("no cluster permission should be authorized",
Role.builder(getRolesResponse.roles()[0]).build().cluster().check("cluster:admin/bar", null, null));
Role.builder(getRolesResponse.roles()[0], null).build().cluster().check("cluster:admin/bar"));
}
}
@ -374,8 +367,8 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
logger.error("--> creating role");
c.preparePutRole("test_role")
.cluster("all")
.addIndices(new String[]{"*"}, new String[]{"read"},
new FieldPermissions(new String[]{"body", "title"}, null), new BytesArray("{\"match_all\": {}}"))
.addIndices(new String[]{"*"}, new String[]{"read"}, new String[]{"body", "title"}, null,
new BytesArray("{\"match_all\": {}}"))
.get();
c.preparePutUser("joe", "s3krit".toCharArray(), "test_role").get();
logger.error("--> waiting for .security index");
@ -401,11 +394,11 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
// create some roles
client.preparePutRole("admin_role")
.cluster("all")
.addIndices(new String[]{"*"}, new String[]{"all"}, new FieldPermissions(), null)
.addIndices(new String[]{"*"}, new String[]{"all"}, null, null, null)
.get();
client.preparePutRole("read_role")
.cluster("none")
.addIndices(new String[]{"*"}, new String[]{"read"}, new FieldPermissions(), null)
.addIndices(new String[]{"*"}, new String[]{"read"}, null, null, null)
.get();
assertThat(client.prepareGetUsers("joes").get().hasUsers(), is(false));
@ -501,7 +494,7 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
} else {
client.preparePutRole("read_role")
.cluster("none")
.addIndices(new String[]{"*"}, new String[]{"read"}, new FieldPermissions(), null)
.addIndices(new String[]{"*"}, new String[]{"read"}, null, null, null)
.get();
}
@ -562,7 +555,7 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
}
public void testOperationsOnReservedRoles() throws Exception {
final String name = randomFrom(SuperuserRole.NAME, KibanaRole.NAME, LogstashSystemRole.NAME);
final String name = randomFrom(ReservedRolesStore.names());
IllegalArgumentException exception = expectThrows(IllegalArgumentException.class,
() -> securityClient().preparePutRole(name).cluster("monitor").get());
assertThat(exception.getMessage(), containsString("role [" + name + "] is reserved"));
@ -616,22 +609,25 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
SecurityClient client = new SecurityClient(client());
PutRoleResponse putRoleResponse = client.preparePutRole("admin_role")
.cluster("all")
.addIndices(new String[]{"*"}, new String[]{"all"}, new FieldPermissions(), null)
.addIndices(new String[]{"*"}, new String[]{"all"}, null, null, null)
.get();
assertThat(putRoleResponse.isCreated(), is(true));
roles++;
if (fls) {
PutRoleResponse roleResponse;
String[] fields = new String[]{"foo"};
FieldPermissions fieldPermissions;
final String[] grantedFields;
final String[] deniedFields;
if (randomBoolean()) {
fieldPermissions = new FieldPermissions(fields, null);
grantedFields = fields;
deniedFields = null;
} else {
fieldPermissions = new FieldPermissions(null, fields);
grantedFields = null;
deniedFields = fields;
}
roleResponse = client.preparePutRole("admin_role_fls")
.cluster("all")
.addIndices(new String[]{"*"}, new String[]{"all"}, fieldPermissions, null)
.addIndices(new String[]{"*"}, new String[]{"all"}, grantedFields, deniedFields, null)
.get();
assertThat(roleResponse.isCreated(), is(true));
roles++;
@ -640,7 +636,7 @@ public class NativeRealmIntegTests extends NativeRealmIntegTestCase {
if (dls) {
PutRoleResponse roleResponse = client.preparePutRole("admin_role_dls")
.cluster("all")
.addIndices(new String[]{"*"}, new String[]{"all"}, new FieldPermissions(), new BytesArray("{ \"match_all\": {} }"))
.addIndices(new String[]{"*"}, new String[]{"all"}, null, null, new BytesArray("{ \"match_all\": {} }"))
.get();
assertThat(roleResponse.isCreated(), is(true));
roles++;

View File

@ -12,6 +12,8 @@ import org.elasticsearch.action.CompositeIndicesRequest;
import org.elasticsearch.action.MockIndicesRequest;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthRequest;
import org.elasticsearch.action.admin.cluster.state.ClusterStateAction;
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsAction;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesAction;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesRequest;
@ -63,6 +65,7 @@ import org.elasticsearch.action.termvectors.TermVectorsAction;
import org.elasticsearch.action.termvectors.TermVectorsRequest;
import org.elasticsearch.action.update.UpdateAction;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.Client;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
@ -72,26 +75,35 @@ import org.elasticsearch.common.collect.Tuple;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.license.GetLicenseAction;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.security.SecurityTemplateService;
import org.elasticsearch.xpack.security.action.user.AuthenticateAction;
import org.elasticsearch.xpack.security.action.user.AuthenticateRequest;
import org.elasticsearch.xpack.security.action.user.AuthenticateRequestBuilder;
import org.elasticsearch.xpack.security.action.user.ChangePasswordAction;
import org.elasticsearch.xpack.security.action.user.ChangePasswordRequest;
import org.elasticsearch.xpack.security.action.user.ChangePasswordRequestBuilder;
import org.elasticsearch.xpack.security.action.user.DeleteUserAction;
import org.elasticsearch.xpack.security.action.user.PutUserAction;
import org.elasticsearch.xpack.security.action.user.UserRequest;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
import org.elasticsearch.xpack.security.authc.DefaultAuthenticationFailureHandler;
import org.elasticsearch.xpack.security.authc.esnative.NativeRealm;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.authc.file.FileRealm;
import org.elasticsearch.xpack.security.authc.ldap.LdapRealm;
import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
import org.elasticsearch.xpack.security.authz.RoleDescriptor.IndicesPrivileges;
import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl.IndexAccessControl;
import org.elasticsearch.xpack.security.authz.permission.DefaultRole;
import org.elasticsearch.xpack.security.authz.permission.GlobalPermission;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.permission.SuperuserRole;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.GeneralPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
@ -100,25 +112,22 @@ import org.junit.Before;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.elasticsearch.test.SecurityTestsUtils.assertAuthenticationException;
import static org.elasticsearch.test.SecurityTestsUtils.assertThrowsAuthorizationException;
import static org.elasticsearch.test.SecurityTestsUtils.assertThrowsAuthorizationExceptionRunAs;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
@ -133,7 +142,7 @@ public class AuthorizationServiceTests extends ESTestCase {
private AuthorizationService authorizationService;
private ThreadContext threadContext;
private ThreadPool threadPool;
private Map<String, Role> roleMap = new HashMap<>();
private Map<String, RoleDescriptor> roleMap = new HashMap<>();
private CompositeRolesStore rolesStore;
@Before
@ -144,12 +153,27 @@ public class AuthorizationServiceTests extends ESTestCase {
threadContext = new ThreadContext(Settings.EMPTY);
threadPool = mock(ThreadPool.class);
when(threadPool.getThreadContext()).thenReturn(threadContext);
final FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY);
doAnswer((i) -> {
ActionListener callback =
(ActionListener) i.getArguments()[1];
callback.onResponse(roleMap.get(i.getArguments()[0]));
ActionListener<Role> callback =
(ActionListener<Role>) i.getArguments()[2];
Set<String> names = (Set<String>) i.getArguments()[0];
assertNotNull(names);
Set<RoleDescriptor> roleDescriptors = new HashSet<>();
for (String name : names) {
RoleDescriptor descriptor = roleMap.get(name);
if (descriptor != null) {
roleDescriptors.add(descriptor);
}
}
if (roleDescriptors.isEmpty()) {
callback.onResponse(Role.EMPTY);
} else {
callback.onResponse(CompositeRolesStore.buildRoleFromDescriptors(roleDescriptors, fieldPermissionsCache));
}
return Void.TYPE;
}).when(rolesStore).roles(any(String.class), any(ActionListener.class));
}).when(rolesStore).roles(any(Set.class), any(FieldPermissionsCache.class), any(ActionListener.class));
authorizationService = new AuthorizationService(Settings.EMPTY, rolesStore, clusterService,
auditTrail, new DefaultAuthenticationFailureHandler(), threadPool, new AnonymousUser(Settings.EMPTY));
}
@ -228,7 +252,8 @@ public class AuthorizationServiceTests extends ESTestCase {
public void testThatNonIndicesAndNonClusterActionIsDenied() {
TransportRequest request = mock(TransportRequest.class);
User user = new User("test user", "a_all");
roleMap.put("a_all", Role.builder("a_role").add(IndexPrivilege.ALL, "a").build());
roleMap.put("a_all", new RoleDescriptor("a_role",null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() },null));
assertThrowsAuthorizationException(
() -> authorize(createAuthentication(user), "whatever", request),
@ -240,7 +265,7 @@ public class AuthorizationServiceTests extends ESTestCase {
public void testThatRoleWithNoIndicesIsDenied() {
TransportRequest request = new IndicesExistsRequest("a");
User user = new User("test user", "no_indices");
roleMap.put("no_indices", Role.builder("no_indices").cluster(ClusterPrivilege.action("")).build());
roleMap.put("no_indices", new RoleDescriptor("a_role",null,null,null));
mockEmptyMetaData();
assertThrowsAuthorizationException(
@ -252,7 +277,8 @@ public class AuthorizationServiceTests extends ESTestCase {
public void testSearchAgainstEmptyCluster() {
User user = new User("test user", "a_all");
roleMap.put("a_all", Role.builder("a_role").add(IndexPrivilege.ALL, "a").build());
roleMap.put("a_all", new RoleDescriptor("a_role", null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() }, null));
mockEmptyMetaData();
{
@ -283,7 +309,8 @@ public class AuthorizationServiceTests extends ESTestCase {
public void testScrollRelatedRequestsAllowed() {
User user = new User("test user", "a_all");
roleMap.put("a_all", Role.builder("a_role").add(IndexPrivilege.ALL, "a").build());
roleMap.put("a_all", new RoleDescriptor("a_role", null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() }, null));
mockEmptyMetaData();
ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
@ -317,7 +344,8 @@ public class AuthorizationServiceTests extends ESTestCase {
TransportRequest request = new GetIndexRequest().indices("b");
ClusterState state = mockEmptyMetaData();
User user = new User("test user", "a_all");
roleMap.put("a_all", Role.builder("a_all").add(IndexPrivilege.ALL, "a").build());
roleMap.put("a_all", new RoleDescriptor("a_role",null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() },null));
assertThrowsAuthorizationException(
() -> authorize(createAuthentication(user), "indices:a", request),
@ -333,7 +361,8 @@ public class AuthorizationServiceTests extends ESTestCase {
request.alias(new Alias("a2"));
ClusterState state = mockEmptyMetaData();
User user = new User("test user", "a_all");
roleMap.put("a_all", Role.builder("a_all").add(IndexPrivilege.ALL, "a").build());
roleMap.put("a_all", new RoleDescriptor("a_role",null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() },null));
assertThrowsAuthorizationException(
() -> authorize(createAuthentication(user), CreateIndexAction.NAME, request),
@ -349,7 +378,8 @@ public class AuthorizationServiceTests extends ESTestCase {
request.alias(new Alias("a2"));
ClusterState state = mockEmptyMetaData();
User user = new User("test user", "a_all");
roleMap.put("a_all", Role.builder("a_all").add(IndexPrivilege.ALL, "a", "a2").build());
roleMap.put("a_all", new RoleDescriptor("a_all",null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a", "a2").privileges("all").build() },null));
authorize(createAuthentication(user), CreateIndexAction.NAME, request);
@ -367,7 +397,8 @@ public class AuthorizationServiceTests extends ESTestCase {
authorizationService = new AuthorizationService(settings, rolesStore, clusterService, auditTrail,
new DefaultAuthenticationFailureHandler(), threadPool, anonymousUser);
roleMap.put("a_all", Role.builder("a_all").add(IndexPrivilege.ALL, "a").build());
roleMap.put("a_all", new RoleDescriptor("a_all",null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() },null));
assertThrowsAuthorizationException(
() -> authorize(createAuthentication(anonymousUser), "indices:a", request),
@ -389,7 +420,8 @@ public class AuthorizationServiceTests extends ESTestCase {
authorizationService = new AuthorizationService(settings, rolesStore, clusterService, auditTrail,
new DefaultAuthenticationFailureHandler(), threadPool, new AnonymousUser(settings));
roleMap.put("a_all", Role.builder("a_all").add(IndexPrivilege.ALL, "a").build());
roleMap.put("a_all", new RoleDescriptor("a_all",null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() },null));
ElasticsearchSecurityException securityException = expectThrows(ElasticsearchSecurityException.class,
() -> authorize(createAuthentication(anonymousUser), "indices:a", request));
@ -405,7 +437,8 @@ public class AuthorizationServiceTests extends ESTestCase {
TransportRequest request = new GetIndexRequest().indices("not-an-index-*").indicesOptions(options);
ClusterState state = mockEmptyMetaData();
User user = new User("test user", "a_all");
roleMap.put("a_all", Role.builder("a_all").add(IndexPrivilege.ALL, "a").build());
roleMap.put("a_all", new RoleDescriptor("a_all",null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() },null));
final IndexNotFoundException nfe = expectThrows(
IndexNotFoundException.class,
@ -431,7 +464,7 @@ public class AuthorizationServiceTests extends ESTestCase {
public void testRunAsRequestWithoutLookedUpBy() {
AuthenticateRequest request = new AuthenticateRequest("run as me");
roleMap.put("can run as", SuperuserRole.INSTANCE);
roleMap.put("can run as", ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR);
User user = new User("test user", new String[] { "can run as" }, new User("run as me", Strings.EMPTY_ARRAY));
Authentication authentication = new Authentication(user, new RealmRef("foo", "bar", "baz"), null);
assertThat(user.runAs(), is(notNullValue()));
@ -446,11 +479,9 @@ public class AuthorizationServiceTests extends ESTestCase {
TransportRequest request = mock(TransportRequest.class);
User user = new User("test user", new String[] { "can run as" }, new User("run as me", "doesn't exist"));
assertThat(user.runAs(), is(notNullValue()));
roleMap.put("can run as", Role
.builder("can run as")
.runAs(new GeneralPrivilege("", "not the right user"))
.add(IndexPrivilege.ALL, "a")
.build());
roleMap.put("can run as", new RoleDescriptor("can run as",null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() },
new String[] { "not the right user" }));
assertThrowsAuthorizationExceptionRunAs(
() -> authorize(createAuthentication(user), "indices:a", request),
@ -463,11 +494,9 @@ public class AuthorizationServiceTests extends ESTestCase {
TransportRequest request = new GetIndexRequest().indices("a");
User user = new User("test user", new String[] { "can run as" }, new User("run as me", "b"));
assertThat(user.runAs(), is(notNullValue()));
roleMap.put("can run as", Role
.builder("can run as")
.runAs(new GeneralPrivilege("", "run as me"))
.add(IndexPrivilege.ALL, "a")
.build());
roleMap.put("can run as", new RoleDescriptor("can run as",null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() },
new String[] { "run as me" }));
if (randomBoolean()) {
ClusterState state = mock(ClusterState.class);
@ -477,10 +506,8 @@ public class AuthorizationServiceTests extends ESTestCase {
.settings(Settings.builder().put("index.version.created", Version.CURRENT).build())
.numberOfShards(1).numberOfReplicas(0).build(), true)
.build());
roleMap.put("b", Role
.builder("b")
.add(IndexPrivilege.ALL, "b")
.build());
roleMap.put("b", new RoleDescriptor("b",null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("b").privileges("all").build() }, null));
} else {
mockEmptyMetaData();
}
@ -497,11 +524,9 @@ public class AuthorizationServiceTests extends ESTestCase {
TransportRequest request = new GetIndexRequest().indices("b");
User user = new User("test user", new String[] { "can run as" }, new User("run as me", "b"));
assertThat(user.runAs(), is(notNullValue()));
roleMap.put("can run as", Role
.builder("can run as")
.runAs(new GeneralPrivilege("", "run as me"))
.add(IndexPrivilege.ALL, "a")
.build());
roleMap.put("can run as", new RoleDescriptor("can run as",null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() },
new String[] { "run as me" }));
ClusterState state = mock(ClusterState.class);
when(clusterService.state()).thenReturn(state);
when(state.metaData()).thenReturn(MetaData.builder()
@ -509,10 +534,8 @@ public class AuthorizationServiceTests extends ESTestCase {
.settings(Settings.builder().put("index.version.created", Version.CURRENT).build())
.numberOfShards(1).numberOfReplicas(0).build(), true)
.build());
roleMap.put("b", Role
.builder("b")
.add(IndexPrivilege.ALL, "b")
.build());
roleMap.put("b", new RoleDescriptor("b",null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("b").privileges("all").build() }, null));
authorize(createAuthentication(user), "indices:a", request);
verify(auditTrail).runAsGranted(user, "indices:a", request);
@ -522,10 +545,8 @@ public class AuthorizationServiceTests extends ESTestCase {
public void testNonXPackUserCannotExecuteOperationAgainstSecurityIndex() {
User user = new User("all_access_user", "all_access");
roleMap.put("all_access", Role.builder("all_access")
.add(IndexPrivilege.ALL, "*")
.cluster(ClusterPrivilege.ALL)
.build());
roleMap.put("all_access", new RoleDescriptor("all access",new String[] { "all" },
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("*").privileges("all").build() }, null));
ClusterState state = mock(ClusterState.class);
when(clusterService.state()).thenReturn(state);
when(state.metaData()).thenReturn(MetaData.builder()
@ -577,10 +598,8 @@ public class AuthorizationServiceTests extends ESTestCase {
public void testGrantedNonXPackUserCanExecuteMonitoringOperationsAgainstSecurityIndex() {
User user = new User("all_access_user", "all_access");
roleMap.put("all_access", Role.builder("all_access")
.add(IndexPrivilege.ALL, "*")
.cluster(ClusterPrivilege.ALL)
.build());
roleMap.put("all_access", new RoleDescriptor("all access",new String[] { "all" },
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("*").privileges("all").build() }, null));
ClusterState state = mock(ClusterState.class);
when(clusterService.state()).thenReturn(state);
when(state.metaData()).thenReturn(MetaData.builder()
@ -609,8 +628,8 @@ public class AuthorizationServiceTests extends ESTestCase {
}
public void testXPackUserAndSuperusersCanExecuteOperationAgainstSecurityIndex() {
final User superuser = new User("custom_admin", SuperuserRole.NAME);
roleMap.put(SuperuserRole.NAME, Role.builder(SuperuserRole.DESCRIPTOR).build());
final User superuser = new User("custom_admin", ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR.getName());
roleMap.put(ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR.getName(), ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR);
ClusterState state = mock(ClusterState.class);
when(clusterService.state()).thenReturn(state);
when(state.metaData()).thenReturn(MetaData.builder()
@ -646,8 +665,8 @@ public class AuthorizationServiceTests extends ESTestCase {
}
public void testXPackUserAndSuperusersCanExecuteOperationAgainstSecurityIndexWithWildcard() {
final User superuser = new User("custom_admin", SuperuserRole.NAME);
roleMap.put(SuperuserRole.NAME, Role.builder(SuperuserRole.DESCRIPTOR).build());
final User superuser = new User("custom_admin", ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR.getName());
roleMap.put(ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR.getName(), ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR);
ClusterState state = mock(ClusterState.class);
when(clusterService.state()).thenReturn(state);
when(state.metaData()).thenReturn(MetaData.builder()
@ -674,10 +693,8 @@ public class AuthorizationServiceTests extends ESTestCase {
final AnonymousUser anonymousUser = new AnonymousUser(settings);
authorizationService = new AuthorizationService(settings, rolesStore, clusterService, auditTrail,
new DefaultAuthenticationFailureHandler(), threadPool, anonymousUser);
roleMap.put("anonymous_user_role", Role.builder("anonymous_user_role")
.cluster(ClusterPrivilege.ALL)
.add(IndexPrivilege.ALL, "a")
.build());
roleMap.put("anonymous_user_role", new RoleDescriptor("anonymous_user_role",new String[] { "all" },
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() }, null));
mockEmptyMetaData();
// sanity check the anonymous user
@ -691,44 +708,24 @@ public class AuthorizationServiceTests extends ESTestCase {
}
public void testDefaultRoleUserWithoutRoles() {
PlainActionFuture<Collection<Role>> rolesFuture = new PlainActionFuture<>();
PlainActionFuture<Role> rolesFuture = new PlainActionFuture<>();
authorizationService.roles(new User("no role user"), rolesFuture);
final Collection<Role> roles = rolesFuture.actionGet();
assertEquals(1, roles.size());
assertEquals(DefaultRole.NAME, roles.iterator().next().name());
final Role roles = rolesFuture.actionGet();
assertEquals(Role.EMPTY, roles);
}
public void testDefaultRoleUserWithoutRolesAnonymousUserEnabled() {
public void testAnonymousUserEnabledRoleAdded() {
Settings settings = Settings.builder().put(AnonymousUser.ROLES_SETTING.getKey(), "anonymous_user_role").build();
final AnonymousUser anonymousUser = new AnonymousUser(settings);
authorizationService = new AuthorizationService(settings, rolesStore, clusterService, auditTrail,
new DefaultAuthenticationFailureHandler(), threadPool, anonymousUser);
roleMap.put("anonymous_user_role", Role.builder("anonymous_user_role")
.cluster(ClusterPrivilege.ALL)
.add(IndexPrivilege.ALL, "a")
.build());
roleMap.put("anonymous_user_role", new RoleDescriptor("anonymous_user_role",new String[] { "all" },
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() }, null));
mockEmptyMetaData();
PlainActionFuture<Collection<Role>> rolesFuture = new PlainActionFuture<>();
PlainActionFuture<Role> rolesFuture = new PlainActionFuture<>();
authorizationService.roles(new User("no role user"), rolesFuture);
final Collection<Role> roles = rolesFuture.actionGet();
assertEquals(2, roles.size());
for (Role role : roles) {
assertThat(role.name(), either(equalTo(DefaultRole.NAME)).or(equalTo("anonymous_user_role")));
}
}
public void testDefaultRoleUserWithSomeRole() {
roleMap.put("role", Role.builder("role")
.cluster(ClusterPrivilege.ALL)
.add(IndexPrivilege.ALL, "a")
.build());
PlainActionFuture<Collection<Role>> rolesFuture = new PlainActionFuture<>();
authorizationService.roles(new User("user with role", "role"), rolesFuture);
final Collection<Role> roles = rolesFuture.actionGet();
assertEquals(2, roles.size());
for (Role role : roles) {
assertThat(role.name(), either(equalTo(DefaultRole.NAME)).or(equalTo("role")));
}
final Role roles = rolesFuture.actionGet();
assertThat(roles.name(), containsString("anonymous_user_role"));
}
public void testCompositeActionsAreImmediatelyRejected() {
@ -737,7 +734,7 @@ public class AuthorizationServiceTests extends ESTestCase {
String action = compositeRequest.v1();
TransportRequest request = compositeRequest.v2();
User user = new User("test user", "no_indices");
roleMap.put("no_indices", Role.builder("no_indices").cluster(ClusterPrivilege.action("")).build());
roleMap.put("no_indices", new RoleDescriptor("no_indices", null, null, null));
assertThrowsAuthorizationException(
() -> authorize(createAuthentication(user), action, request), action, "test user");
verify(auditTrail).accessDenied(user, action, request);
@ -750,8 +747,9 @@ public class AuthorizationServiceTests extends ESTestCase {
String action = compositeRequest.v1();
TransportRequest request = compositeRequest.v2();
User user = new User("test user", "role");
roleMap.put("role", Role.builder("role").add(IndexPrivilege.ALL,
randomBoolean() ? "a" : "index").build());
roleMap.put("role", new RoleDescriptor("role", null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices(randomBoolean() ? "a" : "index").privileges("all").build() },
null));
authorize(createAuthentication(user), action, request);
verify(auditTrail).accessGranted(user, action, request);
verifyNoMoreInteractions(auditTrail);
@ -761,8 +759,9 @@ public class AuthorizationServiceTests extends ESTestCase {
String action = randomCompositeRequest().v1();
TransportRequest request = mock(TransportRequest.class);
User user = new User("test user", "role");
roleMap.put("role", Role.builder("role").add(IndexPrivilege.ALL,
randomBoolean() ? "a" : "index").build());
roleMap.put("role", new RoleDescriptor("role", null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices(randomBoolean() ? "a" : "index").privileges("all").build() },
null));
IllegalStateException illegalStateException = expectThrows(IllegalStateException.class,
() -> authorize(createAuthentication(user), action, request));
assertThat(illegalStateException.getMessage(), containsString("Composite actions must implement CompositeIndicesRequest"));
@ -797,15 +796,162 @@ public class AuthorizationServiceTests extends ESTestCase {
TransportRequest request = new MockIndicesRequest(IndicesOptions.strictExpandOpen(), "index");
User userAllowed = new User("userAllowed", "roleAllowed");
roleMap.put("roleAllowed", Role.builder("roleAllowed").add(IndexPrivilege.ALL, "index").build());
roleMap.put("roleAllowed", new RoleDescriptor("roleAllowed", null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("index").privileges("all").build() }, null));
User userDenied = new User("userDenied", "roleDenied");
roleMap.put("roleDenied", Role.builder("roleDenied").add(IndexPrivilege.ALL, "a").build());
roleMap.put("roleDenied", new RoleDescriptor("roleDenied", null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a").privileges("all").build() }, null));
mockEmptyMetaData();
authorize(createAuthentication(userAllowed), action, request);
assertThrowsAuthorizationException(
() -> authorize(createAuthentication(userDenied), action, request), action, "userDenied");
}
public void testSameUserPermission() {
final User user = new User("joe");
final boolean changePasswordRequest = randomBoolean();
final TransportRequest request = changePasswordRequest ?
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;
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(request, instanceOf(UserRequest.class));
assertTrue(AuthorizationService.checkSameUserPermissions(action, request, authentication));
}
public void testSameUserPermissionDoesNotAllowNonMatchingUsername() {
final User user = new User("joe");
final boolean changePasswordRequest = randomBoolean();
final String username = randomFrom("", "joe" + randomAsciiOfLengthBetween(1, 5), randomAsciiOfLengthBetween(3, 10));
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);
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(request, instanceOf(UserRequest.class));
assertFalse(AuthorizationService.checkSameUserPermissions(action, request, authentication));
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
assertFalse(AuthorizationService.checkSameUserPermissions(action, request, authentication));
if (request instanceof ChangePasswordRequest) {
((ChangePasswordRequest)request).username("joe");
} else {
((AuthenticateRequest)request).username("joe");
}
assertTrue(AuthorizationService.checkSameUserPermissions(action, request, authentication));
}
public void testSameUserPermissionDoesNotAllowOtherActions() {
final User user = mock(User.class);
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));
assertFalse(AuthorizationService.checkSameUserPermissions(action, request, authentication));
verifyZeroInteractions(user, request, authentication);
}
public void testSameUserPermissionRunAsChecksAuthenticatedBy() {
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(authentication.isRunAs()).thenReturn(true);
when(lookedUpBy.getType())
.thenReturn(changePasswordRequest ? randomFrom(ReservedRealm.TYPE, NativeRealm.TYPE) : randomAsciiOfLengthBetween(4, 12));
assertTrue(AuthorizationService.checkSameUserPermissions(action, request, authentication));
when(authentication.getRunAsUser()).thenReturn(user);
assertFalse(AuthorizationService.checkSameUserPermissions(action, request, authentication));
}
public void testSameUserPermissionDoesNotAllowChangePasswordForOtherRealms() {
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.isRunAs()).thenReturn(false);
when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
when(authenticatedBy.getType()).thenReturn(randomFrom(LdapRealm.LDAP_TYPE, FileRealm.TYPE, LdapRealm.AD_TYPE, PkiRealm.TYPE,
randomAsciiOfLengthBetween(4, 12)));
assertThat(request, instanceOf(UserRequest.class));
assertFalse(AuthorizationService.checkSameUserPermissions(action, request, authentication));
verify(authenticatedBy).getType();
verify(authentication).getRunAsUser();
verify(authentication).getAuthenticatedBy();
verify(authentication).isRunAs();
verifyNoMoreInteractions(authenticatedBy, authentication);
}
public void testSameUserPermissionDoesNotAllowChangePasswordForLookedUpByOtherRealms() {
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.isRunAs()).thenReturn(true);
when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
when(authentication.getLookedUpBy()).thenReturn(lookedUpBy);
when(lookedUpBy.getType()).thenReturn(randomFrom(LdapRealm.LDAP_TYPE, FileRealm.TYPE, LdapRealm.AD_TYPE, PkiRealm.TYPE,
randomAsciiOfLengthBetween(4, 12)));
assertThat(request, instanceOf(UserRequest.class));
assertFalse(AuthorizationService.checkSameUserPermissions(action, request, authentication));
verify(authentication).getLookedUpBy();
verify(authentication).getRunAsUser();
verify(authentication).isRunAs();
verify(lookedUpBy).getType();
verifyNoMoreInteractions(authentication, lookedUpBy, authenticatedBy);
}
private static Tuple<String, TransportRequest> randomCompositeRequest() {
switch(randomIntBetween(0, 7)) {
case 0:
@ -833,57 +979,13 @@ public class AuthorizationServiceTests extends ESTestCase {
}
public void testDoesNotUseRolesStoreForXPackUser() {
PlainActionFuture<Collection<Role>> rolesFuture = new PlainActionFuture<>();
PlainActionFuture<Role> rolesFuture = new PlainActionFuture<>();
authorizationService.roles(XPackUser.INSTANCE, rolesFuture);
final Collection<Role> roles = rolesFuture.actionGet();
assertThat(roles, contains(SuperuserRole.INSTANCE));
final Role roles = rolesFuture.actionGet();
assertThat(roles, equalTo(ReservedRolesStore.SUPERUSER_ROLE));
verifyZeroInteractions(rolesStore);
}
public void testPermissionIncludesAnonymousUserPermissions() {
Settings settings = Settings.builder().put(AnonymousUser.ROLES_SETTING.getKey(), "a_all").build();
final AnonymousUser anonymousUser = new AnonymousUser(settings);
authorizationService = new AuthorizationService(settings, rolesStore, clusterService, auditTrail,
new DefaultAuthenticationFailureHandler(), threadPool, anonymousUser);
final boolean roleExists = randomBoolean();
final Role anonymousRole = Role.builder("a_all").add(IndexPrivilege.ALL, "a").build();
if (roleExists) {
roleMap.put("a_all", anonymousRole);
}
final MetaData metaData = MetaData.builder()
.put(new IndexMetaData.Builder("a")
.settings(Settings.builder().put("index.version.created", Version.CURRENT).build())
.numberOfShards(1).numberOfReplicas(0).build(), true)
.build();
User user = new User("no_roles");
PlainActionFuture<Collection<Role>> rolesFuture = new PlainActionFuture<>();
authorizationService.roles(user, rolesFuture);
final Collection<Role> roles = rolesFuture.actionGet();
GlobalPermission globalPermission = authorizationService.permission(roles);
verify(rolesStore).roles(eq("a_all"), any(ActionListener.class));
if (roleExists) {
assertThat(roles, containsInAnyOrder(anonymousRole, DefaultRole.INSTANCE));
assertFalse(globalPermission.isEmpty());
// by default all users have a DefaultRole that grants cluster actions like change password
assertFalse(globalPermission.cluster().isEmpty());
assertFalse(globalPermission.indices().isEmpty());
Map<String, IndexAccessControl> authzMap =
globalPermission.indices().authorize(SearchAction.NAME, Collections.singleton("a"), metaData);
assertTrue(authzMap.containsKey("a"));
assertTrue(authzMap.get("a").isGranted());
assertFalse(authzMap.get("a").getFieldPermissions().hasFieldLevelSecurity());
assertNull(authzMap.get("a").getQueries());
} else {
assertThat(roles, contains(DefaultRole.INSTANCE));
assertFalse(globalPermission.isEmpty());
// by default all users have a DefaultRole that grants cluster actions like change password
assertFalse(globalPermission.cluster().isEmpty());
assertTrue(globalPermission.indices().isEmpty());
}
}
public void testGetRolesForSystemUserThrowsException() {
IllegalArgumentException iae = expectThrows(IllegalArgumentException.class, () -> authorizationService.roles(SystemUser.INSTANCE,
null));

View File

@ -11,14 +11,15 @@ import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.security.authz.RoleDescriptor.IndicesPrivileges;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.xpack.security.user.User;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.hamcrest.Matchers.containsInAnyOrder;
@ -27,15 +28,18 @@ public class AuthorizedIndicesTests extends ESTestCase {
public void testAuthorizedIndicesUserWithoutRoles() {
User user = new User("test user");
AuthorizedIndices authorizedIndices = new AuthorizedIndices(user, Collections.emptyList(), "", MetaData.EMPTY_META_DATA);
AuthorizedIndices authorizedIndices = new AuthorizedIndices(user, Role.EMPTY, "",
MetaData.EMPTY_META_DATA);
List<String> list = authorizedIndices.get();
assertTrue(list.isEmpty());
}
public void testAuthorizedIndicesUserWithSomeRoles() {
User user = new User("test user", "a_star", "b");
Role aStarRole = Role.builder("a_star").add(IndexPrivilege.ALL, "a*").build();
Role bRole = Role.builder("b").add(IndexPrivilege.READ, "b").build();
RoleDescriptor aStarRole = new RoleDescriptor("a_star", null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("a*").privileges("all").build() }, null);
RoleDescriptor bRole = new RoleDescriptor("b", null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("b").privileges("READ").build() }, null);
Settings indexSettings = Settings.builder().put("index.version.created", Version.CURRENT).build();
MetaData metaData = MetaData.builder()
.put(new IndexMetaData.Builder("a1").settings(indexSettings).numberOfShards(1).numberOfReplicas(0).build(), true)
@ -50,7 +54,8 @@ public class AuthorizedIndicesTests extends ESTestCase {
.putAlias(new AliasMetaData.Builder("ba").build())
.build(), true)
.build();
Collection<Role> roles = Arrays.asList(aStarRole, bRole);
Role roles =
CompositeRolesStore.buildRoleFromDescriptors(Sets.newHashSet(aStarRole, bRole), new FieldPermissionsCache(Settings.EMPTY));
AuthorizedIndices authorizedIndices = new AuthorizedIndices(user, roles, SearchAction.NAME, metaData);
List<String> list = authorizedIndices.get();
assertThat(list, containsInAnyOrder("a1", "a2", "aaaaaa", "b", "ab"));
@ -61,8 +66,7 @@ public class AuthorizedIndicesTests extends ESTestCase {
public void testAuthorizedIndicesUserWithSomeRolesEmptyMetaData() {
User user = new User("test user", "role");
Role role = Role.builder("role").add(IndexPrivilege.ALL, "*").build();
Collection<Role> roles = Collections.singletonList(role);
AuthorizedIndices authorizedIndices = new AuthorizedIndices(user, roles, SearchAction.NAME, MetaData.EMPTY_META_DATA);
AuthorizedIndices authorizedIndices = new AuthorizedIndices(user, role, SearchAction.NAME, MetaData.EMPTY_META_DATA);
List<String> list = authorizedIndices.get();
assertTrue(list.isEmpty());
}

View File

@ -5,6 +5,7 @@
*/
package org.elasticsearch.xpack.security.authz;
import org.elasticsearch.ElasticsearchSecurityException;
import org.elasticsearch.action.admin.indices.alias.Alias;
import org.elasticsearch.action.admin.indices.alias.IndicesAliasesAction;
import org.elasticsearch.action.admin.indices.alias.get.GetAliasesAction;
@ -105,9 +106,8 @@ public class IndexAliasesTests extends SecurityIntegTestCase {
client().filterWithHeader(headers).admin().indices().prepareAliases().addAlias("test_1", "test_alias")::get,
IndicesAliasesAction.NAME, "create_only");
IndexNotFoundException indexNotFoundException = expectThrows(IndexNotFoundException.class,
client().filterWithHeader(headers).admin().indices().prepareAliases().addAlias("test_*", "test_alias")::get);
assertThat(indexNotFoundException.toString(), containsString("[test_*]"));
assertThrowsAuthorizationException(client().filterWithHeader(headers).admin().indices().prepareAliases()
.addAlias("test_*", "test_alias")::get, IndicesAliasesAction.NAME, "create_only");
}
public void testCreateIndexAndAliasesCreateOnlyPermission() {
@ -130,13 +130,11 @@ public class IndexAliasesTests extends SecurityIntegTestCase {
client().filterWithHeader(headers).admin().indices().prepareAliases().removeAlias("test_1", "alias_1")::get,
IndicesAliasesAction.NAME, "create_only");
IndexNotFoundException indexNotFoundException = expectThrows(IndexNotFoundException.class,
client().filterWithHeader(headers).admin().indices().prepareAliases().removeAlias("test_1", "alias_*")::get);
assertThat(indexNotFoundException.toString(), containsString("[alias_*"));
assertThrowsAuthorizationException(client().filterWithHeader(headers).admin().indices().prepareAliases()
.removeAlias("test_1", "alias_*")::get, IndicesAliasesAction.NAME, "create_only");
indexNotFoundException = expectThrows(IndexNotFoundException.class,
client().filterWithHeader(headers).admin().indices().prepareAliases().removeAlias("test_1", "_all")::get);
assertThat(indexNotFoundException.toString(), containsString("[_all]"));
assertThrowsAuthorizationException(client().filterWithHeader(headers).admin().indices().prepareAliases()
.removeAlias("test_1", "_all")::get, IndicesAliasesAction.NAME, "create_only");
}
public void testGetAliasesCreateOnlyPermissionStrict() {
@ -147,24 +145,21 @@ public class IndexAliasesTests extends SecurityIntegTestCase {
assertThrowsAuthorizationException(client().filterWithHeader(headers).admin().indices().prepareGetAliases("test_1")
.setIndices("test_1").setIndicesOptions(IndicesOptions.strictExpand())::get, GetAliasesAction.NAME, "create_only");
IndexNotFoundException indexNotFoundException = expectThrows(IndexNotFoundException.class, client().filterWithHeader(headers)
assertThrowsAuthorizationException(client().filterWithHeader(headers)
.admin().indices().prepareGetAliases("_all")
.setIndices("test_1").setIndicesOptions(IndicesOptions.strictExpand())::get);
assertThat(indexNotFoundException.toString(), containsString("[_all]"));
.setIndices("test_1").setIndicesOptions(IndicesOptions.strictExpand())::get, GetAliasesAction.NAME, "create_only");
indexNotFoundException = expectThrows(IndexNotFoundException.class, client().filterWithHeader(headers).admin().indices()
.prepareGetAliases().setIndices("test_1").setIndicesOptions(IndicesOptions.strictExpand())::get);
assertThat(indexNotFoundException.toString(), containsString("[_all]"));
assertThrowsAuthorizationException(client().filterWithHeader(headers).admin().indices()
.prepareGetAliases().setIndices("test_1").setIndicesOptions(IndicesOptions.strictExpand())::get,
GetAliasesAction.NAME, "create_only");
GetAliasesResponse getAliasesResponse = client().filterWithHeader(headers).admin().indices().prepareGetAliases("test_alias")
.setIndices("test_*").setIndicesOptions(IndicesOptions.strictExpand()).get();
assertEquals(0, getAliasesResponse.getAliases().size());
assertThrowsAuthorizationException(client().filterWithHeader(headers).admin().indices().prepareGetAliases("test_alias")
.setIndices("test_*").setIndicesOptions(IndicesOptions.strictExpand())::get, GetAliasesAction.NAME, "create_only");
//this throws exception no matter what the indices options are because the aliases part cannot be resolved to any alias
//and there is no way to "allow_no_aliases" like we can do with indices.
indexNotFoundException = expectThrows(IndexNotFoundException.class,
client().filterWithHeader(headers).admin().indices().prepareGetAliases()::get);
assertThat(indexNotFoundException.toString(), containsString("[_all]"));
assertThrowsAuthorizationException(client().filterWithHeader(headers).admin().indices().prepareGetAliases()::get,
GetAliasesAction.NAME, "create_only");
}
public void testGetAliasesCreateOnlyPermissionIgnoreUnavailable() {
@ -172,28 +167,23 @@ public class IndexAliasesTests extends SecurityIntegTestCase {
Map<String, String> headers = Collections.singletonMap(BASIC_AUTH_HEADER, basicAuthHeaderValue("create_only",
new SecuredString("test123".toCharArray())));
GetAliasesResponse getAliasesResponse = client().filterWithHeader(headers).admin().indices().prepareGetAliases("test_1")
.setIndices("test_1").setIndicesOptions(IndicesOptions.lenientExpandOpen()).get();
assertEquals(0, getAliasesResponse.getAliases().size());
assertThrowsAuthorizationException(client().filterWithHeader(headers).admin().indices().prepareGetAliases("test_1")
.setIndices("test_1").setIndicesOptions(IndicesOptions.lenientExpandOpen())::get, GetAliasesAction.NAME, "create_only");
IndexNotFoundException indexNotFoundException = expectThrows(IndexNotFoundException.class, client().filterWithHeader(headers)
.admin().indices().prepareGetAliases("_all")
.setIndices("test_1").setIndicesOptions(IndicesOptions.lenientExpandOpen())::get);
assertThat(indexNotFoundException.toString(), containsString("[_all]"));
assertThrowsAuthorizationException(client().filterWithHeader(headers).admin().indices().prepareGetAliases("_all")
.setIndices("test_1").setIndicesOptions(IndicesOptions.lenientExpandOpen())::get, GetAliasesAction.NAME, "create_only");
indexNotFoundException = expectThrows(IndexNotFoundException.class, client().filterWithHeader(headers).admin().indices()
.prepareGetAliases().setIndices("test_1").setIndicesOptions(IndicesOptions.lenientExpandOpen())::get);
assertThat(indexNotFoundException.toString(), containsString("[_all]"));
assertThrowsAuthorizationException(client().filterWithHeader(headers).admin().indices().prepareGetAliases().setIndices("test_1")
.setIndicesOptions(IndicesOptions.lenientExpandOpen())::get, GetAliasesAction.NAME, "create_only");
getAliasesResponse = client().filterWithHeader(headers).admin().indices().prepareGetAliases("test_alias")
.setIndices("test_*").setIndicesOptions(IndicesOptions.lenientExpandOpen()).get();
assertEquals(0, getAliasesResponse.getAliases().size());
assertThrowsAuthorizationException(
client().filterWithHeader(headers).admin().indices().prepareGetAliases("test_alias")
.setIndices("test_*").setIndicesOptions(IndicesOptions.lenientExpandOpen())::get, GetAliasesAction.NAME, "create_only");
//this throws exception no matter what the indices options are because the aliases part cannot be resolved to any alias
//and there is no way to "allow_no_aliases" like we can do with indices.
indexNotFoundException = expectThrows(IndexNotFoundException.class, client().filterWithHeader(headers).admin().indices()
.prepareGetAliases().setIndicesOptions(IndicesOptions.lenientExpandOpen())::get);
assertThat(indexNotFoundException.toString(), containsString("[_all]"));
assertThrowsAuthorizationException(client().filterWithHeader(headers).admin().indices()
.prepareGetAliases().setIndicesOptions(IndicesOptions.lenientExpandOpen())::get, GetAliasesAction.NAME, "create_only");
}
public void testCreateIndexThenAliasesCreateAndAliasesPermission() {

View File

@ -41,19 +41,19 @@ import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.security.SecurityTemplateService;
import org.elasticsearch.xpack.security.audit.AuditTrailService;
import org.elasticsearch.xpack.security.authc.DefaultAuthenticationFailureHandler;
import org.elasticsearch.xpack.security.authz.RoleDescriptor.IndicesPrivileges;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.permission.SuperuserRole;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
import org.elasticsearch.xpack.security.authz.store.CompositeRolesStore;
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
import org.elasticsearch.xpack.security.user.AnonymousUser;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.user.XPackUser;
import org.junit.Before;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@ -77,7 +77,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase {
private AuthorizationService authzService;
private IndicesAndAliasesResolver defaultIndicesResolver;
private IndexNameExpressionResolver indexNameExpressionResolver;
private Map<String, Role> roleMap;
private Map<String, RoleDescriptor> roleMap;
@Before
public void setup() {
@ -113,16 +113,33 @@ public class IndicesAndAliasesResolverTests extends ESTestCase {
String[] authorizedIndices = new String[] { "bar", "bar-closed", "foofoobar", "foofoo", "missing", "foofoo-closed"};
String[] dashIndices = new String[]{"-index10", "-index11", "-index20", "-index21"};
roleMap = new HashMap<>();
roleMap.put("role", Role.builder("role").add(IndexPrivilege.ALL, authorizedIndices).build());
roleMap.put("dash", Role.builder("dash").add(IndexPrivilege.ALL, dashIndices).build());
roleMap.put("test", Role.builder("test").cluster(ClusterPrivilege.MONITOR).build());
roleMap.put(SuperuserRole.NAME, Role.builder(SuperuserRole.DESCRIPTOR).build());
roleMap.put("role", new RoleDescriptor("role", null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices(authorizedIndices).privileges("all").build() }, null));
roleMap.put("dash", new RoleDescriptor("dash", null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices(dashIndices).privileges("all").build() }, null));
roleMap.put("test", new RoleDescriptor("role", new String[] { "monitor" }, null, null));
roleMap.put(ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR.getName(), ReservedRolesStore.SUPERUSER_ROLE_DESCRIPTOR);
final FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY);
doAnswer((i) -> {
ActionListener callback =
(ActionListener) i.getArguments()[1];
callback.onResponse(roleMap.get(i.getArguments()[0]));
(ActionListener) i.getArguments()[2];
Set<String> names = (Set<String>) i.getArguments()[0];
assertNotNull(names);
Set<RoleDescriptor> roleDescriptors = new HashSet<>();
for (String name : names) {
RoleDescriptor descriptor = roleMap.get(name);
if (descriptor != null) {
roleDescriptors.add(descriptor);
}
}
if (roleDescriptors.isEmpty()) {
callback.onResponse(Role.EMPTY);
} else {
callback.onResponse(CompositeRolesStore.buildRoleFromDescriptors(roleDescriptors, fieldPermissionsCache));
}
return Void.TYPE;
}).when(rolesStore).roles(any(String.class), any(ActionListener.class));
}).when(rolesStore).roles(any(Set.class), any(FieldPermissionsCache.class), any(ActionListener.class));
ClusterService clusterService = mock(ClusterService.class);
authzService = new AuthorizationService(settings, rolesStore, clusterService,
@ -1048,8 +1065,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase {
public void testNonXPackUserAccessingSecurityIndex() {
User allAccessUser = new User("all_access", "all_access");
roleMap.put("all_access",
Role.builder("all_access").add(IndexPrivilege.ALL, "*").cluster(ClusterPrivilege.ALL).build());
roleMap.put("all_access", new RoleDescriptor("all_access", new String[] { "all" },
new IndicesPrivileges[] { IndicesPrivileges.builder().indices("*").privileges("all").build() }, null));
{
SearchRequest request = new SearchRequest();
@ -1093,7 +1110,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase {
// make the user authorized
String dateTimeIndex = indexNameExpressionResolver.resolveDateMathExpression("<datetime-{now/M}>");
String[] authorizedIndices = new String[] { "bar", "bar-closed", "foofoobar", "foofoo", "missing", "foofoo-closed", dateTimeIndex};
roleMap.put("role", Role.builder("role").add(IndexPrivilege.ALL, authorizedIndices).build());
roleMap.put("role", new RoleDescriptor("role", null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices(authorizedIndices).privileges("all").build() }, null));
SearchRequest request = new SearchRequest("<datetime-{now/M}>");
if (randomBoolean()) {
@ -1130,7 +1148,8 @@ public class IndicesAndAliasesResolverTests extends ESTestCase {
// make the user authorized
String[] authorizedIndices = new String[] { "bar", "bar-closed", "foofoobar", "foofoo", "missing", "foofoo-closed",
indexNameExpressionResolver.resolveDateMathExpression("<datetime-{now/M}>")};
roleMap.put("role", Role.builder("role").add(IndexPrivilege.ALL, authorizedIndices).build());
roleMap.put("role", new RoleDescriptor("role", null,
new IndicesPrivileges[] { IndicesPrivileges.builder().indices(authorizedIndices).privileges("all").build() }, null));
GetAliasesRequest request = new GetAliasesRequest("<datetime-{now/M}>").indices("foo", "foofoo");
Set<String> indices = defaultIndicesResolver.resolve(request, metaData, buildAuthorizedIndices(user, GetAliasesAction.NAME));
//the union of all indices and aliases gets returned
@ -1144,7 +1163,7 @@ public class IndicesAndAliasesResolverTests extends ESTestCase {
// TODO with the removal of DeleteByQuery is there another way to test resolving a write action?
private AuthorizedIndices buildAuthorizedIndices(User user, String action) {
PlainActionFuture<Collection<Role>> rolesListener = new PlainActionFuture<>();
PlainActionFuture<Role> rolesListener = new PlainActionFuture<>();
authzService.roles(user, rolesListener);
return new AuthorizedIndices(user, rolesListener.actionGet(), action, metaData);
}

View File

@ -39,7 +39,7 @@ public class RoleDescriptorTests extends ESTestCase {
RoleDescriptor.IndicesPrivileges.builder()
.indices("i1", "i2")
.privileges("read")
.fieldPermissions(new FieldPermissions(new String[]{"body", "title"}, null))
.grantedFields("body", "title")
.query("{\"query\": {\"match_all\": {}}}")
.build()
};
@ -54,7 +54,7 @@ public class RoleDescriptorTests extends ESTestCase {
RoleDescriptor.IndicesPrivileges.builder()
.indices("i1", "i2")
.privileges("read")
.fieldPermissions(new FieldPermissions(new String[]{"body", "title"}, null))
.grantedFields("body", "title")
.query("{\"query\": {\"match_all\": {}}}")
.build()
};
@ -119,7 +119,7 @@ public class RoleDescriptorTests extends ESTestCase {
RoleDescriptor.IndicesPrivileges.builder()
.indices("i1", "i2")
.privileges("read")
.fieldPermissions(new FieldPermissions(new String[]{"body", "title"}, null))
.grantedFields("body", "title")
.query("{\"query\": {\"match_all\": {}}}")
.build()
};

View File

@ -5,21 +5,9 @@
*/
package org.elasticsearch.xpack.security.authz.accesscontrol;
import org.apache.lucene.util.automaton.Automaton;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl.IndexAccessControl;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
import java.util.Collections;
import java.util.Set;
import static org.hamcrest.Matchers.contains;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
/**
* Unit tests for {@link IndicesAccessControl}
@ -28,164 +16,7 @@ public class IndicesAccessControlTests extends ESTestCase {
public void testEmptyIndicesAccessControl() {
IndicesAccessControl indicesAccessControl = new IndicesAccessControl(true, Collections.emptyMap());
assertThat(indicesAccessControl.isGranted(), is(true));
assertThat(indicesAccessControl.getIndexPermissions(randomAsciiOfLengthBetween(3,20)), nullValue());
}
public void testMergeFields() {
IndexAccessControl indexAccessControl = new IndexAccessControl(true, new FieldPermissions(new String[]{"a", "c"}, null), null);
IndexAccessControl other = new IndexAccessControl(true,new FieldPermissions(new String[]{"b"}, null), null);
IndexAccessControl merge1 = indexAccessControl.merge(other);
assertTrue(merge1.getFieldPermissions().grantsAccessTo("a"));
assertTrue(merge1.getFieldPermissions().grantsAccessTo("b"));
assertTrue(merge1.getFieldPermissions().grantsAccessTo("c"));
assertTrue(merge1.getFieldPermissions().hasFieldLevelSecurity());
assertThat(merge1.isGranted(), is(true));
assertThat(merge1.getQueries(), nullValue());
IndexAccessControl merge2 = other.merge(indexAccessControl);
assertTrue(merge2.getFieldPermissions().grantsAccessTo("a"));
assertTrue(merge2.getFieldPermissions().grantsAccessTo("b"));
assertTrue(merge2.getFieldPermissions().grantsAccessTo("c"));
assertTrue(merge2.getFieldPermissions().hasFieldLevelSecurity());
assertThat(merge2.isGranted(), is(true));
assertThat(merge2.getQueries(), nullValue());
}
public void testMergeEmptyAndNullFields() {
IndexAccessControl indexAccessControl = new IndexAccessControl(true, new FieldPermissions(new String[]{}, null), null);
IndexAccessControl other = new IndexAccessControl(true, new FieldPermissions(), null);
IndexAccessControl merge1 = indexAccessControl.merge(other);
assertFalse(merge1.getFieldPermissions().hasFieldLevelSecurity());
assertThat(merge1.isGranted(), is(true));
assertThat(merge1.getQueries(), nullValue());
IndexAccessControl merge2 = other.merge(indexAccessControl);
assertFalse(merge1.getFieldPermissions().hasFieldLevelSecurity());
assertThat(merge2.isGranted(), is(true));
assertThat(merge2.getQueries(), nullValue());
}
public void testMergeNullFields() {
IndexAccessControl indexAccessControl = new IndexAccessControl(true, new FieldPermissions(new String[]{"a", "b"}, null), null);
IndexAccessControl other = new IndexAccessControl(true, new FieldPermissions(), null);
IndexAccessControl merge1 = indexAccessControl.merge(other);
assertFalse(merge1.getFieldPermissions().hasFieldLevelSecurity());
assertThat(merge1.isGranted(), is(true));
assertThat(merge1.getQueries(), nullValue());
IndexAccessControl merge2 = other.merge(indexAccessControl);
assertFalse(merge1.getFieldPermissions().hasFieldLevelSecurity());
assertThat(merge2.isGranted(), is(true));
assertThat(merge2.getQueries(), nullValue());
}
public void testMergeQueries() {
BytesReference query1 = new BytesArray(new byte[] { 0x1 });
BytesReference query2 = new BytesArray(new byte[] { 0x2 });
IndexAccessControl indexAccessControl = new IndexAccessControl(true, new FieldPermissions(), Collections.singleton
(query1));
IndexAccessControl other = new IndexAccessControl(true, new FieldPermissions(), Collections.singleton(query2));
IndexAccessControl merge1 = indexAccessControl.merge(other);
assertFalse(merge1.getFieldPermissions().hasFieldLevelSecurity());
assertThat(merge1.isGranted(), is(true));
assertThat(merge1.getQueries(), containsInAnyOrder(query1, query2));
IndexAccessControl merge2 = other.merge(indexAccessControl);
assertFalse(merge1.getFieldPermissions().hasFieldLevelSecurity());
assertThat(merge2.isGranted(), is(true));
assertThat(merge1.getQueries(), containsInAnyOrder(query1, query2));
}
public void testMergeNullQuery() {
BytesReference query1 = new BytesArray(new byte[] { 0x1 });
IndexAccessControl indexAccessControl = new IndexAccessControl(true, new FieldPermissions(), Collections.singleton
(query1));
IndexAccessControl other = new IndexAccessControl(true, new FieldPermissions(), null);
IndexAccessControl merge1 = indexAccessControl.merge(other);
assertFalse(merge1.getFieldPermissions().hasFieldLevelSecurity());
assertThat(merge1.isGranted(), is(true));
assertThat(merge1.getQueries(), nullValue());
IndexAccessControl merge2 = other.merge(indexAccessControl);
assertFalse(merge1.getFieldPermissions().hasFieldLevelSecurity());
assertThat(merge2.isGranted(), is(true));
assertThat(merge1.getQueries(), nullValue());
}
public void testMergeNotGrantedAndGranted() {
final String[] notGrantedFields = randomFrom(new String[]{}, new String[]{"baz"}, null);
final Set<BytesReference> notGrantedQueries = randomFrom(Collections.<BytesReference>emptySet(), null,
Collections.<BytesReference>singleton(new BytesArray(new byte[] { randomByte() })));
final IndexAccessControl indexAccessControl = new IndexAccessControl(false, new FieldPermissions(notGrantedFields, null),
notGrantedQueries);
final BytesReference query1 = new BytesArray(new byte[] { 0x1 });
final String[] fields =
randomFrom(new String[]{"foo"}, new String[]{"foo", "bar"}, new String[]{}, null);
final Set<BytesReference> queries =
randomFrom(Collections.singleton(query1), Collections.<BytesReference>emptySet(), null);
final IndexAccessControl other = new IndexAccessControl(true, new FieldPermissions(fields, null), queries);
IndexAccessControl merged = indexAccessControl.merge(other);
assertThat(merged.isGranted(), is(true));
assertThat(merged.getQueries(), equalTo(queries));
if (fields == null) {
assertFalse(merged.getFieldPermissions().hasFieldLevelSecurity());
} else {
assertTrue(merged.getFieldPermissions().hasFieldLevelSecurity());
if (notGrantedFields != null) {
for (String field : notGrantedFields) {
assertFalse(merged.getFieldPermissions().grantsAccessTo(field));
}
}
for (String field : fields) {
assertTrue(merged.getFieldPermissions().grantsAccessTo(field));
}
}
merged = other.merge(indexAccessControl);
assertThat(merged.isGranted(), is(true));
assertThat(merged.getQueries(), equalTo(queries));
if (fields == null) {
assertFalse(merged.getFieldPermissions().hasFieldLevelSecurity());
} else {
assertTrue(merged.getFieldPermissions().hasFieldLevelSecurity());
if (notGrantedFields != null) {
for (String field : notGrantedFields) {
assertFalse(merged.getFieldPermissions().grantsAccessTo(field));
}
}
for (String field : fields) {
assertTrue(merged.getFieldPermissions().grantsAccessTo(field));
}
}
}
public void testMergeNotGranted() {
final String[] notGrantedFields = randomFrom(new String[]{}, new String[]{"baz"}, null);
final Set<BytesReference> notGrantedQueries = randomFrom(Collections.<BytesReference>emptySet(), null,
Collections.<BytesReference>singleton(new BytesArray(new byte[] { randomByte() })));
final IndexAccessControl indexAccessControl = new IndexAccessControl(false, new FieldPermissions(notGrantedFields, null),
notGrantedQueries);
final BytesReference query1 = new BytesArray(new byte[] { 0x1 });
final String[] fields =
randomFrom(new String[]{"foo"}, new String[]{"foo", "bar"}, new String[]{}, null);
final Set<BytesReference> queries =
randomFrom(Collections.singleton(query1), Collections.<BytesReference>emptySet(), null);
final IndexAccessControl other = new IndexAccessControl(false, new FieldPermissions(fields, null), queries);
IndexAccessControl merged = indexAccessControl.merge(other);
assertThat(merged.isGranted(), is(false));
assertThat(merged.getQueries(), equalTo(notGrantedQueries));
merged = other.merge(indexAccessControl);
assertThat(merged.isGranted(), is(false));
assertThat(merged.getQueries(), equalTo(queries));
assertTrue(indicesAccessControl.isGranted());
assertNull(indicesAccessControl.getIndexPermissions(randomAsciiOfLengthBetween(3,20)));
}
}

View File

@ -19,12 +19,15 @@ import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
import org.elasticsearch.xpack.security.authz.permission.IndicesPermission;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.notNullValue;
@ -41,21 +44,24 @@ public class IndicesPermissionTests extends ESTestCase {
)
.putAlias(AliasMetaData.builder("_alias"));
MetaData md = MetaData.builder().put(imbBuilder).build();
FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY);
// basics:
BytesReference query = new BytesArray("{}");
Set<BytesReference> query = Collections.singleton(new BytesArray("{}"));
String[] fields = new String[]{"_field"};
Role role = Role.builder("_role").add(new FieldPermissions(fields, null), query, IndexPrivilege.ALL, "_index").build();
IndicesAccessControl permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md);
Role role = Role.builder("_role")
.add(new FieldPermissions(fields, null), query, IndexPrivilege.ALL, "_index").build();
IndicesAccessControl permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md, fieldPermissionsCache);
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
assertTrue(permissions.getIndexPermissions("_index").getFieldPermissions().grantsAccessTo("_field"));
assertTrue(permissions.getIndexPermissions("_index").getFieldPermissions().hasFieldLevelSecurity());
assertThat(permissions.getIndexPermissions("_index").getQueries().size(), equalTo(1));
assertThat(permissions.getIndexPermissions("_index").getQueries().iterator().next(), equalTo(query));
assertThat(permissions.getIndexPermissions("_index").getQueries(), equalTo(query));
// no document level security:
role = Role.builder("_role").add(new FieldPermissions(fields, null), null, IndexPrivilege.ALL, "_index").build();
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md);
role = Role.builder("_role")
.add(new FieldPermissions(fields, null), null, IndexPrivilege.ALL, "_index").build();
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md, fieldPermissionsCache);
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
assertTrue(permissions.getIndexPermissions("_index").getFieldPermissions().grantsAccessTo("_field"));
assertTrue(permissions.getIndexPermissions("_index").getFieldPermissions().hasFieldLevelSecurity());
@ -63,48 +69,64 @@ public class IndicesPermissionTests extends ESTestCase {
// no field level security:
role = Role.builder("_role").add(new FieldPermissions(), query, IndexPrivilege.ALL, "_index").build();
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md);
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md, fieldPermissionsCache);
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
assertFalse(permissions.getIndexPermissions("_index").getFieldPermissions().hasFieldLevelSecurity());
assertThat(permissions.getIndexPermissions("_index").getQueries().size(), equalTo(1));
assertThat(permissions.getIndexPermissions("_index").getQueries().iterator().next(), equalTo(query));
assertThat(permissions.getIndexPermissions("_index").getQueries(), equalTo(query));
// index group associated with an alias:
role = Role.builder("_role").add(new FieldPermissions(fields, null), query, IndexPrivilege.ALL, "_alias").build();
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_alias"), md);
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_alias"), md, fieldPermissionsCache);
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
assertTrue(permissions.getIndexPermissions("_index").getFieldPermissions().grantsAccessTo("_field"));
assertTrue(permissions.getIndexPermissions("_index").getFieldPermissions().hasFieldLevelSecurity());
assertThat(permissions.getIndexPermissions("_index").getQueries().size(), equalTo(1));
assertThat(permissions.getIndexPermissions("_index").getQueries().iterator().next(), equalTo(query));
assertThat(permissions.getIndexPermissions("_index").getQueries(), equalTo(query));
// match all fields
String[] allFields = randomFrom(new String[]{"*"}, new String[]{"foo", "*"},
new String[]{randomAsciiOfLengthBetween(1, 10), "*"});
role = Role.builder("_role").add(new FieldPermissions(allFields, null), query, IndexPrivilege.ALL, "_alias").build();
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_alias"), md);
role = Role.builder("_role")
.add(new FieldPermissions(allFields, null), query, IndexPrivilege.ALL, "_alias").build();
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_alias"), md, fieldPermissionsCache);
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
assertFalse(permissions.getIndexPermissions("_index").getFieldPermissions().hasFieldLevelSecurity());
assertThat(permissions.getIndexPermissions("_index").getQueries().size(), equalTo(1));
assertThat(permissions.getIndexPermissions("_index").getQueries().iterator().next(), equalTo(query));
assertThat(permissions.getIndexPermissions("_index").getQueries(), equalTo(query));
}
public void testIndicesPriviledgesStreaming() throws IOException {
public void testIndicesPrivilegesStreaming() throws IOException {
BytesStreamOutput out = new BytesStreamOutput();
String[] allowed = new String[]{randomAsciiOfLength(5) + "*", randomAsciiOfLength(5) + "*", randomAsciiOfLength(5) + "*"};
String[] denied = new String[]{allowed[0] + randomAsciiOfLength(5), allowed[1] + randomAsciiOfLength(5),
allowed[2] + randomAsciiOfLength(5)};
FieldPermissions fieldPermissions = new FieldPermissions(allowed, denied);
RoleDescriptor.IndicesPrivileges.Builder indicesPrivileges = RoleDescriptor.IndicesPrivileges.builder();
indicesPrivileges.fieldPermissions(fieldPermissions);
indicesPrivileges.grantedFields(allowed);
indicesPrivileges.deniedFields(denied);
indicesPrivileges.query("{match_all:{}}");
indicesPrivileges.indices(randomAsciiOfLength(5), randomAsciiOfLength(5), randomAsciiOfLength(5));
indicesPrivileges.privileges("all", "read", "priv");
indicesPrivileges.build().writeTo(out);
out.close();
StreamInput in = out.bytes().streamInput();
RoleDescriptor.IndicesPrivileges readIndicesPriviledges = RoleDescriptor.IndicesPrivileges.createFrom(in);
assertEquals(readIndicesPriviledges, indicesPrivileges.build());
RoleDescriptor.IndicesPrivileges readIndicesPrivileges = RoleDescriptor.IndicesPrivileges.createFrom(in);
assertEquals(readIndicesPrivileges, indicesPrivileges.build());
out = new BytesStreamOutput();
out.setVersion(Version.V_5_0_0);
indicesPrivileges = RoleDescriptor.IndicesPrivileges.builder();
indicesPrivileges.grantedFields(allowed);
indicesPrivileges.deniedFields(denied);
indicesPrivileges.query("{match_all:{}}");
indicesPrivileges.indices(readIndicesPrivileges.getIndices());
indicesPrivileges.privileges("all", "read", "priv");
indicesPrivileges.build().writeTo(out);
out.close();
in = out.bytes().streamInput();
in.setVersion(Version.V_5_0_0);
RoleDescriptor.IndicesPrivileges readIndicesPrivileges2 = RoleDescriptor.IndicesPrivileges.createFrom(in);
assertEquals(readIndicesPrivileges, readIndicesPrivileges2);
}
// tests that field permissions are merged correctly when we authorize with several groups and don't crash when an index has no group
@ -115,12 +137,13 @@ public class IndicesPermissionTests extends ESTestCase {
.put(new IndexMetaData.Builder("a2").settings(indexSettings).numberOfShards(1).numberOfReplicas(0).build(), true)
.build();
FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY);
IndicesPermission.Group group1 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(), null, "a1");
IndicesPermission.Group group2 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(null, new
String[]{"denied_field"}), null, "a1");
IndicesPermission.Core core = new IndicesPermission.Core(group1, group2);
IndicesPermission core = new IndicesPermission(group1, group2);
Map<String, IndicesAccessControl.IndexAccessControl> authzMap =
core.authorize(SearchAction.NAME, Sets.newHashSet("a1", "ba"), metaData);
core.authorize(SearchAction.NAME, Sets.newHashSet("a1", "ba"), metaData, fieldPermissionsCache);
assertTrue(authzMap.get("a1").getFieldPermissions().grantsAccessTo("denied_field"));
assertTrue(authzMap.get("a1").getFieldPermissions().grantsAccessTo(randomAsciiOfLength(5)));
// did not define anything for ba so we allow all
@ -137,8 +160,8 @@ public class IndicesPermissionTests extends ESTestCase {
, new String[]{"denied_field"}), null, "a2");
IndicesPermission.Group group4 = new IndicesPermission.Group(IndexPrivilege.ALL, new FieldPermissions(new String[]{"*_field2"}
, new String[]{"denied_field2"}), null, "a2");
core = new IndicesPermission.Core(group1, group2, group3, group4);
authzMap = core.authorize(SearchAction.NAME, Sets.newHashSet("a1", "a2"), metaData);
core = new IndicesPermission(group1, group2, group3, group4);
authzMap = core.authorize(SearchAction.NAME, Sets.newHashSet("a1", "a2"), metaData, fieldPermissionsCache);
assertFalse(authzMap.get("a1").getFieldPermissions().hasFieldLevelSecurity());
assertFalse(authzMap.get("a2").getFieldPermissions().grantsAccessTo("denied_field2"));
assertFalse(authzMap.get("a2").getFieldPermissions().grantsAccessTo("denied_field"));

View File

@ -1,202 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
import org.elasticsearch.action.admin.cluster.state.ClusterStateAction;
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsAction;
import org.elasticsearch.client.Client;
import org.elasticsearch.license.GetLicenseAction;
import org.elasticsearch.xpack.security.action.user.AuthenticateRequestBuilder;
import org.elasticsearch.xpack.security.action.user.ChangePasswordRequestBuilder;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
import org.elasticsearch.xpack.security.authc.esnative.NativeRealm;
import org.elasticsearch.xpack.security.authc.esnative.ReservedRealm;
import org.elasticsearch.xpack.security.authc.file.FileRealm;
import org.elasticsearch.xpack.security.authc.ldap.LdapRealm;
import org.elasticsearch.xpack.security.authc.pki.PkiRealm;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.action.user.AuthenticateAction;
import org.elasticsearch.xpack.security.action.user.AuthenticateRequest;
import org.elasticsearch.xpack.security.action.user.ChangePasswordAction;
import org.elasticsearch.xpack.security.action.user.ChangePasswordRequest;
import org.elasticsearch.xpack.security.action.user.DeleteUserAction;
import org.elasticsearch.xpack.security.action.user.PutUserAction;
import org.elasticsearch.xpack.security.action.user.UserRequest;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.transport.TransportRequest;
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.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}
*/
public class DefaultRoleTests extends ESTestCase {
public void testDefaultRoleHasNoIndicesPrivileges() {
Iterator<IndicesPermission.Group> iter = DefaultRole.INSTANCE.indices().iterator();
assertThat(iter.hasNext(), is(false));
}
public void testDefaultRoleHasNoRunAsPrivileges() {
assertThat(DefaultRole.INSTANCE.runAs().isEmpty(), is(true));
}
public void testDefaultRoleAllowsUser() {
final User user = new User("joe");
final boolean changePasswordRequest = randomBoolean();
final TransportRequest request = changePasswordRequest ?
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;
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(request, instanceOf(UserRequest.class));
assertThat(DefaultRole.INSTANCE.cluster().check(action, request, authentication), is(true));
}
public void testDefaultRoleDoesNotAllowNonMatchingUsername() {
final User user = new User("joe");
final boolean changePasswordRequest = randomBoolean();
final String username = randomFrom("", "joe" + randomAsciiOfLengthBetween(1, 5), randomAsciiOfLengthBetween(3, 10));
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);
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(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");
}
assertThat(DefaultRole.INSTANCE.cluster().check(action, request, authentication), is(true));
}
public void testDefaultRoleDoesNotAllowOtherActions() {
final User user = mock(User.class);
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, 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(authentication.isRunAs()).thenReturn(true);
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.isRunAs()).thenReturn(false);
when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
when(authenticatedBy.getType()).thenReturn(randomFrom(LdapRealm.LDAP_TYPE, FileRealm.TYPE, LdapRealm.AD_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).getRunAsUser();
verify(authentication).getAuthenticatedBy();
verify(authentication).isRunAs();
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.isRunAs()).thenReturn(true);
when(authentication.getAuthenticatedBy()).thenReturn(authenticatedBy);
when(authentication.getLookedUpBy()).thenReturn(lookedUpBy);
when(lookedUpBy.getType()).thenReturn(randomFrom(LdapRealm.LDAP_TYPE, FileRealm.TYPE, LdapRealm.AD_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).getRunAsUser();
verify(authentication).isRunAs();
verify(lookedUpBy).getType();
verifyNoMoreInteractions(authentication, lookedUpBy, authenticatedBy);
}
}

View File

@ -29,9 +29,8 @@ public class FieldPermissionTests extends ESTestCase {
"\"except\": [\"f3\",\"f4\"]" +
"}}]}";
RoleDescriptor rd = RoleDescriptor.parse("test", new BytesArray(q), false);
assertArrayEquals(rd.getIndicesPrivileges()[0].getFieldPermissions().getGrantedFieldsArray(),
new String[]{"f1", "f2", "f3", "f4"});
assertArrayEquals(rd.getIndicesPrivileges()[0].getFieldPermissions().getDeniedFieldsArray(), new String[]{"f3", "f4"});
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] { "f1", "f2", "f3", "f4" });
assertArrayEquals(rd.getIndicesPrivileges()[0].getDeniedFields(), new String[] { "f3", "f4" });
q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
"\"field_security\": {" +
@ -39,25 +38,24 @@ public class FieldPermissionTests extends ESTestCase {
"\"grant\": [\"f1\", \"f2\", \"f3\", \"f4\"]" +
"}}]}";
rd = RoleDescriptor.parse("test", new BytesArray(q), false);
assertArrayEquals(rd.getIndicesPrivileges()[0].getFieldPermissions().getGrantedFieldsArray(),
new String[]{"f1", "f2", "f3", "f4"});
assertArrayEquals(rd.getIndicesPrivileges()[0].getFieldPermissions().getDeniedFieldsArray(), new String[]{"f3", "f4"});
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] { "f1", "f2", "f3", "f4" });
assertArrayEquals(rd.getIndicesPrivileges()[0].getDeniedFields(), new String[] { "f3", "f4" });
q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
"\"field_security\": {" +
"\"grant\": [\"f1\", \"f2\"]" +
"}}]}";
rd = RoleDescriptor.parse("test", new BytesArray(q), false);
assertArrayEquals(rd.getIndicesPrivileges()[0].getFieldPermissions().getGrantedFieldsArray(), new String[]{"f1", "f2"});
assertNull(rd.getIndicesPrivileges()[0].getFieldPermissions().getDeniedFieldsArray());
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] { "f1", "f2" });
assertNull(rd.getIndicesPrivileges()[0].getDeniedFields());
q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
"\"field_security\": {" +
"\"grant\": []" +
"}}]}";
rd = RoleDescriptor.parse("test", new BytesArray(q), false);
assertArrayEquals(rd.getIndicesPrivileges()[0].getFieldPermissions().getGrantedFieldsArray(), new String[]{});
assertNull(rd.getIndicesPrivileges()[0].getFieldPermissions().getDeniedFieldsArray());
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] {});
assertNull(rd.getIndicesPrivileges()[0].getDeniedFields());
q = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
"\"field_security\": {" +
@ -65,8 +63,8 @@ public class FieldPermissionTests extends ESTestCase {
"\"grant\": []" +
"}}]}";
rd = RoleDescriptor.parse("test", new BytesArray(q), false);
assertArrayEquals(rd.getIndicesPrivileges()[0].getFieldPermissions().getGrantedFieldsArray(), new String[]{});
assertArrayEquals(rd.getIndicesPrivileges()[0].getFieldPermissions().getDeniedFieldsArray(), new String[]{});
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] {});
assertArrayEquals(rd.getIndicesPrivileges()[0].getDeniedFields(), new String[] {});
final String exceptWithoutGrant = "{\"indices\": [ {\"names\": \"idx2\", \"privileges\": [\"p3\"], " +
"\"field_security\": {" +
@ -122,10 +120,10 @@ public class FieldPermissionTests extends ESTestCase {
" \"except\": [\"f2\"]}," +
"\"privileges\": [\"p3\"]}]}";
rd = RoleDescriptor.parse("test", new BytesArray(q), false);
assertArrayEquals(rd.getIndicesPrivileges()[0].getFieldPermissions().getGrantedFieldsArray(), new String[]{});
assertNull(rd.getIndicesPrivileges()[0].getFieldPermissions().getDeniedFieldsArray());
assertArrayEquals(rd.getIndicesPrivileges()[1].getFieldPermissions().getGrantedFieldsArray(), new String[]{"*"});
assertArrayEquals(rd.getIndicesPrivileges()[1].getFieldPermissions().getDeniedFieldsArray(), new String[]{"f2"});
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[] {});
assertNull(rd.getIndicesPrivileges()[0].getDeniedFields());
assertArrayEquals(rd.getIndicesPrivileges()[1].getGrantedFields(), new String[] {"*"});
assertArrayEquals(rd.getIndicesPrivileges()[1].getDeniedFields(), new String[] {"f2"});
}
// test old syntax for field permissions
@ -134,8 +132,8 @@ public class FieldPermissionTests extends ESTestCase {
"\"fields\": [\"f1\", \"f2\"]" +
"}]}";
RoleDescriptor rd = RoleDescriptor.parse("test", new BytesArray(q), true);
assertArrayEquals(rd.getIndicesPrivileges()[0].getFieldPermissions().getGrantedFieldsArray(), new String[]{"f1", "f2"});
assertNull(rd.getIndicesPrivileges()[0].getFieldPermissions().getDeniedFieldsArray());
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[]{"f1", "f2"});
assertNull(rd.getIndicesPrivileges()[0].getDeniedFields());
final String failingQuery = q;
ElasticsearchParseException e = expectThrows(ElasticsearchParseException.class, () -> RoleDescriptor.parse("test", new BytesArray
@ -147,8 +145,8 @@ public class FieldPermissionTests extends ESTestCase {
"\"fields\": []" +
"}]}";
rd = RoleDescriptor.parse("test", new BytesArray(q), true);
assertArrayEquals(rd.getIndicesPrivileges()[0].getFieldPermissions().getGrantedFieldsArray(), new String[]{});
assertNull(rd.getIndicesPrivileges()[0].getFieldPermissions().getDeniedFieldsArray());
assertArrayEquals(rd.getIndicesPrivileges()[0].getGrantedFields(), new String[]{});
assertNull(rd.getIndicesPrivileges()[0].getDeniedFields());
final String failingQuery2 = q;
e = expectThrows(ElasticsearchParseException.class, () -> RoleDescriptor.parse("test", new BytesArray
(failingQuery2), false));
@ -159,76 +157,14 @@ public class FieldPermissionTests extends ESTestCase {
"\"fields\": null" +
"}]}";
rd = RoleDescriptor.parse("test", new BytesArray(q), true);
assertNull(rd.getIndicesPrivileges()[0].getFieldPermissions().getGrantedFieldsArray());
assertNull(rd.getIndicesPrivileges()[0].getFieldPermissions().getDeniedFieldsArray());
assertNull(rd.getIndicesPrivileges()[0].getGrantedFields());
assertNull(rd.getIndicesPrivileges()[0].getDeniedFields());
final String failingQuery3 = q;
e = expectThrows(ElasticsearchParseException.class, () -> RoleDescriptor.parse("test", new BytesArray(failingQuery3), false));
assertThat(e.getDetailedMessage(), containsString("[\"fields\": [...]] format has changed for field permissions in role [test]" +
", use [\"field_security\": {\"grant\":[...],\"except\":[...]}] instead"));
}
public void testMergeFieldPermissions() {
String allowedPrefix1 = randomAsciiOfLength(5);
String allowedPrefix2 = randomAsciiOfLength(5);
String[] allowed1 = new String[]{allowedPrefix1 + "*"};
String[] allowed2 = new String[]{allowedPrefix2 + "*"};
String[] denied1 = new String[]{allowedPrefix1 + "a"};
String[] denied2 = new String[]{allowedPrefix2 + "a"};
FieldPermissions fieldPermissions1 = new FieldPermissions(allowed1, denied1);
FieldPermissions fieldPermissions2 = new FieldPermissions(allowed2, denied2);
FieldPermissions mergedFieldPermissions = FieldPermissions.merge(fieldPermissions1, fieldPermissions2);
assertTrue(mergedFieldPermissions.grantsAccessTo(allowedPrefix1 + "b"));
assertTrue(mergedFieldPermissions.grantsAccessTo(allowedPrefix2 + "b"));
assertFalse(mergedFieldPermissions.grantsAccessTo(denied1[0]));
assertFalse(mergedFieldPermissions.grantsAccessTo(denied2[0]));
allowed1 = new String[]{randomAsciiOfLength(5) + "*", randomAsciiOfLength(5) + "*"};
allowed2 = null;
denied1 = new String[]{allowed1[0] + "a", allowed1[1] + "a"};
denied2 = null;
fieldPermissions1 = new FieldPermissions(allowed1, denied1);
fieldPermissions2 = new FieldPermissions(allowed2, denied2);
mergedFieldPermissions = FieldPermissions.merge(fieldPermissions1, fieldPermissions2);
assertFalse(mergedFieldPermissions.hasFieldLevelSecurity());
allowed1 = new String[]{};
allowed2 = new String[]{randomAsciiOfLength(5) + "*", randomAsciiOfLength(5) + "*"};
denied1 = new String[]{};
denied2 = new String[]{allowed2[0] + "a", allowed2[1] + "a"};
fieldPermissions1 = new FieldPermissions(allowed1, denied1);
fieldPermissions2 = new FieldPermissions(allowed2, denied2);
mergedFieldPermissions = FieldPermissions.merge(fieldPermissions1, fieldPermissions2);
for (String field : allowed2) {
assertTrue(mergedFieldPermissions.grantsAccessTo(field));
}
for (String field : denied2) {
assertFalse(mergedFieldPermissions.grantsAccessTo(field));
}
allowed1 = randomBoolean() ? null : new String[]{"*"};
allowed2 = randomBoolean() ? null : new String[]{"*"};
denied1 = new String[]{"a"};
denied2 = new String[]{"b"};
fieldPermissions1 = new FieldPermissions(allowed1, denied1);
fieldPermissions2 = new FieldPermissions(allowed2, denied2);
mergedFieldPermissions = FieldPermissions.merge(fieldPermissions1, fieldPermissions2);
assertTrue(mergedFieldPermissions.grantsAccessTo("a"));
assertTrue(mergedFieldPermissions.grantsAccessTo("b"));
// test merge does not remove _all
allowed1 = new String[]{"_all"};
allowed2 = new String[]{};
denied1 = null;
denied2 = null;
fieldPermissions1 = new FieldPermissions(allowed1, denied1);
assertTrue(fieldPermissions1.allFieldIsAllowed);
fieldPermissions2 = new FieldPermissions(allowed2, denied2);
assertFalse(fieldPermissions2.allFieldIsAllowed);
mergedFieldPermissions = FieldPermissions.merge(fieldPermissions1, fieldPermissions2);
assertTrue(mergedFieldPermissions.grantsAccessTo("_all"));
assertTrue(mergedFieldPermissions.allFieldIsAllowed);
}
public void testFieldPermissionsStreaming() throws IOException {
BytesStreamOutput out = new BytesStreamOutput();
String[] allowed = new String[]{randomAsciiOfLength(5) + "*", randomAsciiOfLength(5) + "*", randomAsciiOfLength(5) + "*"};
@ -245,10 +181,7 @@ public class FieldPermissionTests extends ESTestCase {
public void testFieldPermissionsHashCodeThreadSafe() throws Exception {
final int numThreads = scaledRandomIntBetween(4, 16);
final FieldPermissions fieldPermissions = randomBoolean() ?
new FieldPermissions(new String[] { "*" }, new String[] { "foo" }) :
FieldPermissions.merge(new FieldPermissions(new String[] { "f*" }, new String[] { "foo" }),
new FieldPermissions(new String[] { "b*" }, new String[] { "bar" }));
final FieldPermissions fieldPermissions = new FieldPermissions(new String[] { "*" }, new String[] { "foo" });
final CountDownLatch latch = new CountDownLatch(numThreads + 1);
final AtomicReferenceArray<Integer> hashCodes = new AtomicReferenceArray<>(numThreads);
List<Thread> threads = new ArrayList<>(numThreads);

View File

@ -0,0 +1,120 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.ESTestCase;
import java.util.Arrays;
public class FieldPermissionsCacheTests extends ESTestCase {
public void testFieldPermissionsCaching() {
FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY);
String[] allowed = new String[]{randomAsciiOfLength(5) + "*", randomAsciiOfLength(5) + "*", randomAsciiOfLength(5) + "*"};
String[] denied = new String[]{allowed[0] + randomAsciiOfLength(5), allowed[1] + randomAsciiOfLength(5),
allowed[2] + randomAsciiOfLength(5)};
FieldPermissions fieldPermissions = fieldPermissionsCache.getFieldPermissions(allowed, denied);
assertNotNull(fieldPermissions);
final String[] allowed2 = randomBoolean() ? allowed : Arrays.copyOf(allowed, allowed.length);
final String[] denied2 = randomBoolean() ? denied : Arrays.copyOf(denied, denied.length);
assertSame(fieldPermissions, fieldPermissionsCache.getFieldPermissions(allowed2, denied2));
}
public void testMergeFieldPermissions() {
FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY);
String allowedPrefix1 = randomAsciiOfLength(5);
String allowedPrefix2 = randomAsciiOfLength(5);
String[] allowed1 = new String[]{allowedPrefix1 + "*"};
String[] allowed2 = new String[]{allowedPrefix2 + "*"};
String[] denied1 = new String[]{allowedPrefix1 + "a"};
String[] denied2 = new String[]{allowedPrefix2 + "a"};
FieldPermissions fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
new FieldPermissions(allowed1, denied1);
FieldPermissions fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
new FieldPermissions(allowed2, denied2);
FieldPermissions mergedFieldPermissions =
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
assertTrue(mergedFieldPermissions.grantsAccessTo(allowedPrefix1 + "b"));
assertTrue(mergedFieldPermissions.grantsAccessTo(allowedPrefix2 + "b"));
assertFalse(mergedFieldPermissions.grantsAccessTo(denied1[0]));
assertFalse(mergedFieldPermissions.grantsAccessTo(denied2[0]));
allowed1 = new String[]{randomAsciiOfLength(5) + "*", randomAsciiOfLength(5) + "*"};
allowed2 = null;
denied1 = new String[]{allowed1[0] + "a", allowed1[1] + "a"};
denied2 = null;
fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
new FieldPermissions(allowed1, denied1);
fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
new FieldPermissions(allowed2, denied2);
mergedFieldPermissions =
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
assertFalse(mergedFieldPermissions.hasFieldLevelSecurity());
allowed1 = new String[]{};
allowed2 = new String[]{randomAsciiOfLength(5) + "*", randomAsciiOfLength(5) + "*"};
denied1 = new String[]{};
denied2 = new String[]{allowed2[0] + "a", allowed2[1] + "a"};
fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
new FieldPermissions(allowed1, denied1);
fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
new FieldPermissions(allowed2, denied2);
mergedFieldPermissions =
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
for (String field : allowed2) {
assertTrue(mergedFieldPermissions.grantsAccessTo(field));
}
for (String field : denied2) {
assertFalse(mergedFieldPermissions.grantsAccessTo(field));
}
allowed1 = randomBoolean() ? null : new String[]{"*"};
allowed2 = randomBoolean() ? null : new String[]{"*"};
denied1 = new String[]{"a"};
denied2 = new String[]{"b"};
fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
new FieldPermissions(allowed1, denied1);
fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
new FieldPermissions(allowed2, denied2);
mergedFieldPermissions =
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
assertTrue(mergedFieldPermissions.grantsAccessTo("a"));
assertTrue(mergedFieldPermissions.grantsAccessTo("b"));
// test merge does not remove _all
allowed1 = new String[]{"_all"};
allowed2 = new String[]{};
denied1 = null;
denied2 = null;
fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
new FieldPermissions(allowed1, denied1);
fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
new FieldPermissions(allowed2, denied2);
mergedFieldPermissions =
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
assertTrue(fieldPermissions1.isAllFieldIsAllowed());
assertFalse(fieldPermissions2.isAllFieldIsAllowed());
assertTrue(mergedFieldPermissions.grantsAccessTo("_all"));
assertTrue(mergedFieldPermissions.isAllFieldIsAllowed());
allowed1 = new String[] { "a*" };
allowed2 = new String[] { "b*" };
denied1 = new String[] { "aa*" };
denied2 = null;
fieldPermissions1 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed1, denied1) :
new FieldPermissions(allowed1, denied1);
fieldPermissions2 = randomBoolean() ? fieldPermissionsCache.getFieldPermissions(allowed2, denied2) :
new FieldPermissions(allowed2, denied2);
mergedFieldPermissions =
fieldPermissionsCache.getFieldPermissions(Arrays.asList(fieldPermissions1, fieldPermissions2));
assertTrue(mergedFieldPermissions.grantsAccessTo("a"));
assertTrue(mergedFieldPermissions.grantsAccessTo("b"));
assertFalse(mergedFieldPermissions.grantsAccessTo("aa"));
assertFalse(mergedFieldPermissions.grantsAccessTo("aa1"));
assertTrue(mergedFieldPermissions.grantsAccessTo("a1"));
}
}

View File

@ -1,56 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteAction;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsAction;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateAction;
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesAction;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
import org.elasticsearch.action.get.GetAction;
import org.elasticsearch.action.index.IndexAction;
import org.elasticsearch.action.ingest.DeletePipelineAction;
import org.elasticsearch.action.ingest.GetPipelineAction;
import org.elasticsearch.action.ingest.PutPipelineAction;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.monitoring.action.MonitoringBulkAction;
import org.elasticsearch.xpack.security.authc.Authentication;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.mock;
public class IngestAdminRoleTests extends ESTestCase {
public void testClusterPermissions() {
final TransportRequest request = new TransportRequest.Empty();
final Authentication authentication = mock(Authentication.class);
assertThat(IngestAdminRole.INSTANCE.cluster().check(PutIndexTemplateAction.NAME, request, authentication), is(true));
assertThat(IngestAdminRole.INSTANCE.cluster().check(GetIndexTemplatesAction.NAME, request, authentication), is(true));
assertThat(IngestAdminRole.INSTANCE.cluster().check(DeleteIndexTemplateAction.NAME, request, authentication), is(true));
assertThat(IngestAdminRole.INSTANCE.cluster().check(PutPipelineAction.NAME, request, authentication), is(true));
assertThat(IngestAdminRole.INSTANCE.cluster().check(GetPipelineAction.NAME, request, authentication), is(true));
assertThat(IngestAdminRole.INSTANCE.cluster().check(DeletePipelineAction.NAME, request, authentication), is(true));
assertThat(IngestAdminRole.INSTANCE.cluster().check(ClusterRerouteAction.NAME, request, authentication), is(false));
assertThat(IngestAdminRole.INSTANCE.cluster().check(ClusterUpdateSettingsAction.NAME, request, authentication), is(false));
assertThat(IngestAdminRole.INSTANCE.cluster().check(MonitoringBulkAction.NAME, request, authentication), is(false));
}
public void testNoIndicesPermissions() {
assertThat(IngestAdminRole.INSTANCE.indices().allowedIndicesMatcher(IndexAction.NAME).test("foo"), is(false));
assertThat(IngestAdminRole.INSTANCE.indices().allowedIndicesMatcher("indices:foo").test(randomAsciiOfLengthBetween(8, 24)),
is(false));
assertThat(IngestAdminRole.INSTANCE.indices().allowedIndicesMatcher(GetAction.NAME).test(randomAsciiOfLengthBetween(8, 24)),
is(false));
}
public void testHasReservedMetadata() {
assertThat(IngestAdminRole.DESCRIPTOR.getMetadata(), hasEntry("_reserved", true));
}
}

View File

@ -1,73 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteAction;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsAction;
import org.elasticsearch.action.admin.cluster.state.ClusterStateAction;
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsAction;
import org.elasticsearch.action.admin.indices.create.CreateIndexAction;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexAction;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsAction;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
import org.elasticsearch.action.delete.DeleteAction;
import org.elasticsearch.action.index.IndexAction;
import org.elasticsearch.xpack.monitoring.action.MonitoringBulkAction;
import org.elasticsearch.xpack.security.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
*/
public class KibanaRoleTests extends ESTestCase {
public void testCluster() {
final TransportRequest request = new TransportRequest.Empty();
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() {
assertThat(KibanaRole.INSTANCE.runAs().isEmpty(), is(true));
}
public void testUnauthorizedIndices() {
assertThat(KibanaRole.INSTANCE.indices().allowedIndicesMatcher(IndexAction.NAME).test("foo"), is(false));
assertThat(KibanaRole.INSTANCE.indices().allowedIndicesMatcher(IndexAction.NAME).test(".reporting"), is(false));
assertThat(KibanaRole.INSTANCE.indices().allowedIndicesMatcher("indices:foo").test(randomAsciiOfLengthBetween(8, 24)), is(false));
}
public void testKibanaIndices() {
Arrays.asList(".kibana", ".kibana-devnull").forEach(this::testAllIndexAccess);
}
public void testReportingIndices() {
testAllIndexAccess(".reporting-" + randomAsciiOfLength(randomIntBetween(0, 13)));
}
private void testAllIndexAccess(String index) {
assertThat(KibanaRole.INSTANCE.indices().allowedIndicesMatcher("indices:foo").test(index), is(true));
assertThat(KibanaRole.INSTANCE.indices().allowedIndicesMatcher("indices:bar").test(index), is(true));
assertThat(KibanaRole.INSTANCE.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(index), is(true));
assertThat(KibanaRole.INSTANCE.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(index), is(true));
assertThat(KibanaRole.INSTANCE.indices().allowedIndicesMatcher(IndexAction.NAME).test(index), is(true));
assertThat(KibanaRole.INSTANCE.indices().allowedIndicesMatcher(DeleteAction.NAME).test(index), is(true));
assertThat(KibanaRole.INSTANCE.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(index), is(true));
}
}

View File

@ -1,72 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteAction;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsAction;
import org.elasticsearch.action.admin.cluster.state.ClusterStateAction;
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsAction;
import org.elasticsearch.action.admin.indices.create.CreateIndexAction;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexAction;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsAction;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
import org.elasticsearch.action.delete.DeleteAction;
import org.elasticsearch.action.index.IndexAction;
import org.elasticsearch.action.search.MultiSearchAction;
import org.elasticsearch.action.search.SearchAction;
import org.elasticsearch.xpack.monitoring.action.MonitoringBulkAction;
import org.elasticsearch.xpack.security.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 Authentication authentication = mock(Authentication.class);
final TransportRequest request = new TransportRequest.Empty();
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() {
assertThat(KibanaUserRole.INSTANCE.runAs().isEmpty(), is(true));
}
public void testUnauthorizedIndices() {
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(IndexAction.NAME).test("foo"), is(false));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(IndexAction.NAME).test(".reporting"), is(false));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher("indices:foo")
.test(randomAsciiOfLengthBetween(8, 24)), is(false));
}
public void testKibanaIndices() {
Arrays.asList(".kibana", ".kibana-devnull").forEach(this::testIndexAccess);
}
private void testIndexAccess(String index) {
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher("indices:foo").test(index), is(false));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher("indices:bar").test(index), is(false));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(DeleteAction.NAME).test(index), is(true));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(index), is(true));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(index), is(true));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(IndexAction.NAME).test(index), is(true));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(SearchAction.NAME).test(index), is(true));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(MultiSearchAction.NAME).test(index), is(true));
assertThat(KibanaUserRole.INSTANCE.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(index), is(true));
}
}

View File

@ -1,56 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import java.util.Arrays;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteAction;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsAction;
import org.elasticsearch.action.admin.cluster.state.ClusterStateAction;
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsAction;
import org.elasticsearch.action.admin.indices.create.CreateIndexAction;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexAction;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsAction;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
import org.elasticsearch.action.delete.DeleteAction;
import org.elasticsearch.action.index.IndexAction;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.monitoring.action.MonitoringBulkAction;
import org.elasticsearch.xpack.security.authc.Authentication;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.mock;
/**
* Tests for the logstash_system role
*/
public class LogstashSystemRoleTests extends ESTestCase {
public void testCluster() {
final TransportRequest request = new TransportRequest.Empty();
final Authentication authentication = mock(Authentication.class);
assertThat(LogstashSystemRole.INSTANCE.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true));
assertThat(LogstashSystemRole.INSTANCE.cluster().check(ClusterStateAction.NAME, request, authentication), is(true));
assertThat(LogstashSystemRole.INSTANCE.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true));
assertThat(LogstashSystemRole.INSTANCE.cluster().check(PutIndexTemplateAction.NAME, request, authentication), is(false));
assertThat(LogstashSystemRole.INSTANCE.cluster().check(ClusterRerouteAction.NAME, request, authentication), is(false));
assertThat(LogstashSystemRole.INSTANCE.cluster().check(ClusterUpdateSettingsAction.NAME, request, authentication), is(false));
assertThat(LogstashSystemRole.INSTANCE.cluster().check(MonitoringBulkAction.NAME, request, authentication), is(true));
}
public void testRunAs() {
assertThat(LogstashSystemRole.INSTANCE.runAs().isEmpty(), is(true));
}
public void testUnauthorizedIndices() {
assertThat(LogstashSystemRole.INSTANCE.indices().allowedIndicesMatcher(IndexAction.NAME).test("foo"), is(false));
assertThat(LogstashSystemRole.INSTANCE.indices().allowedIndicesMatcher(IndexAction.NAME).test(".reporting"), is(false));
assertThat(LogstashSystemRole.INSTANCE.indices().allowedIndicesMatcher("indices:foo").test(randomAsciiOfLengthBetween(8, 24)),
is(false));
}
}

View File

@ -1,74 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteAction;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsAction;
import org.elasticsearch.action.admin.cluster.state.ClusterStateAction;
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsAction;
import org.elasticsearch.action.admin.indices.create.CreateIndexAction;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexAction;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsAction;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
import org.elasticsearch.action.delete.DeleteAction;
import org.elasticsearch.action.get.GetAction;
import org.elasticsearch.action.index.IndexAction;
import org.elasticsearch.action.search.SearchAction;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.monitoring.action.MonitoringBulkAction;
import org.elasticsearch.xpack.security.authc.Authentication;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.mock;
/**
* Tests for the builtin monitoring user
*/
public class MonitoringUserRoleTests extends ESTestCase {
public void testCluster() {
final TransportRequest request = new TransportRequest.Empty();
final Authentication authentication = mock(Authentication.class);
assertThat(MonitoringUserRole.INSTANCE.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false));
assertThat(MonitoringUserRole.INSTANCE.cluster().check(ClusterStateAction.NAME, request, authentication), is(false));
assertThat(MonitoringUserRole.INSTANCE.cluster().check(ClusterStatsAction.NAME, request, authentication), is(false));
assertThat(MonitoringUserRole.INSTANCE.cluster().check(PutIndexTemplateAction.NAME, request, authentication), is(false));
assertThat(MonitoringUserRole.INSTANCE.cluster().check(ClusterRerouteAction.NAME, request, authentication), is(false));
assertThat(MonitoringUserRole.INSTANCE.cluster().check(ClusterUpdateSettingsAction.NAME, request, authentication), is(false));
assertThat(MonitoringUserRole.INSTANCE.cluster().check(MonitoringBulkAction.NAME, request, authentication), is(false));
}
public void testRunAs() {
assertThat(MonitoringUserRole.INSTANCE.runAs().isEmpty(), is(true));
}
public void testUnauthorizedIndices() {
assertThat(MonitoringUserRole.INSTANCE.indices().allowedIndicesMatcher(SearchAction.NAME).test("foo"), is(false));
assertThat(MonitoringUserRole.INSTANCE.indices().allowedIndicesMatcher(SearchAction.NAME).test(".reporting"), is(false));
assertThat(MonitoringUserRole.INSTANCE.indices().allowedIndicesMatcher(SearchAction.NAME).test(".kibana"), is(false));
assertThat(MonitoringUserRole.INSTANCE.indices().allowedIndicesMatcher("indices:foo").test(randomAsciiOfLengthBetween(8, 24)),
is(false));
}
public void testMonitoringIndices() {
testReadAccess(".monitoring-" + randomAsciiOfLength(randomIntBetween(0, 13)));
testReadAccess(".marvel-es-" + randomAsciiOfLength(randomIntBetween(0, 13)));
}
private void testReadAccess(String index) {
assertThat(MonitoringUserRole.INSTANCE.indices().allowedIndicesMatcher("indices:foo").test(index), is(false));
assertThat(MonitoringUserRole.INSTANCE.indices().allowedIndicesMatcher("indices:bar").test(index), is(false));
assertThat(MonitoringUserRole.INSTANCE.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(index), is(false));
assertThat(MonitoringUserRole.INSTANCE.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(index), is(false));
assertThat(MonitoringUserRole.INSTANCE.indices().allowedIndicesMatcher(IndexAction.NAME).test(index), is(false));
assertThat(MonitoringUserRole.INSTANCE.indices().allowedIndicesMatcher(DeleteAction.NAME).test(index), is(false));
assertThat(MonitoringUserRole.INSTANCE.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(index), is(false));
assertThat(MonitoringUserRole.INSTANCE.indices().allowedIndicesMatcher(SearchAction.NAME).test(index), is(true));
assertThat(MonitoringUserRole.INSTANCE.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true));
}
}

View File

@ -6,20 +6,14 @@
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.action.get.GetAction;
import org.elasticsearch.xpack.security.authz.privilege.ClusterPrivilege;
import org.elasticsearch.xpack.security.authz.privilege.GeneralPrivilege;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.security.authz.privilege.Privilege;
import org.junit.Before;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.function.Predicate;
import static org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege.MONITOR;
import static org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege.READ;
import static org.elasticsearch.xpack.security.authz.privilege.IndexPrivilege.union;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
@ -29,9 +23,9 @@ public class PermissionTests extends ESTestCase {
@Before
public void init() {
Role.Builder builder = Role.builder("test");
builder.add(union(MONITOR), "test_*", "/foo.*/");
builder.add(union(READ), "baz_*foo", "/fool.*bar/");
builder.add(union(MONITOR), "/bar.*/");
builder.add(MONITOR, "test_*", "/foo.*/");
builder.add(READ, "baz_*foo", "/fool.*bar/");
builder.add(MONITOR, "/bar.*/");
permission = builder.build();
}
@ -45,23 +39,6 @@ public class PermissionTests extends ESTestCase {
assertThat(matcher1, is(matcher2));
}
public void testIndicesGlobalsIterator() {
Role.Builder builder = Role.builder("tc_role");
builder.cluster(ClusterPrivilege.action("cluster:monitor/nodes/info"));
Role noIndicesPermission = builder.build();
IndicesPermission.Globals indicesGlobals = new IndicesPermission.Globals(
Collections.<GlobalPermission>unmodifiableList(Arrays.asList(noIndicesPermission, permission)));
Iterator<IndicesPermission.Group> iterator = indicesGlobals.iterator();
assertThat(iterator.hasNext(), is(equalTo(true)));
int count = 0;
while (iterator.hasNext()) {
iterator.next();
count++;
}
assertThat(count, is(equalTo(permission.indices().groups().length)));
}
public void testBuildEmptyRole() {
Role.Builder permission = Role.builder("some_role");
Role role = permission.build();
@ -73,7 +50,7 @@ public class PermissionTests extends ESTestCase {
public void testRunAs() {
Role permission = Role.builder("some_role")
.runAs(new GeneralPrivilege("name", "user1", "run*"))
.runAs(new Privilege("name", "user1", "run*"))
.build();
assertThat(permission.runAs().check("user1"), is(true));
assertThat(permission.runAs().check("user"), is(false));

View File

@ -1,77 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteAction;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsAction;
import org.elasticsearch.action.admin.cluster.state.ClusterStateAction;
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsAction;
import org.elasticsearch.action.admin.indices.create.CreateIndexAction;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexAction;
import org.elasticsearch.action.admin.indices.get.GetIndexAction;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsAction;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
import org.elasticsearch.action.delete.DeleteAction;
import org.elasticsearch.action.get.GetAction;
import org.elasticsearch.action.index.IndexAction;
import org.elasticsearch.action.search.SearchAction;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.monitoring.action.MonitoringBulkAction;
import org.elasticsearch.xpack.security.authc.Authentication;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.mock;
/**
* Tests for the remote monitoring agent role
*/
public class RemoteMonitoringAgentRoleTests extends ESTestCase {
public void testCluster() {
final TransportRequest request = new TransportRequest.Empty();
final Authentication authentication = mock(Authentication.class);
assertThat(RemoteMonitoringAgentRole.INSTANCE.cluster().check(ClusterHealthAction.NAME, request, authentication), is(true));
assertThat(RemoteMonitoringAgentRole.INSTANCE.cluster().check(ClusterStateAction.NAME, request, authentication), is(true));
assertThat(RemoteMonitoringAgentRole.INSTANCE.cluster().check(ClusterStatsAction.NAME, request, authentication), is(true));
assertThat(RemoteMonitoringAgentRole.INSTANCE.cluster().check(PutIndexTemplateAction.NAME, request, authentication), is(true));
assertThat(RemoteMonitoringAgentRole.INSTANCE.cluster().check(ClusterRerouteAction.NAME, request, authentication), is(false));
assertThat(RemoteMonitoringAgentRole.INSTANCE.cluster().check(ClusterUpdateSettingsAction.NAME, request, authentication),
is(false));
assertThat(RemoteMonitoringAgentRole.INSTANCE.cluster().check(MonitoringBulkAction.NAME, request, authentication), is(false));
}
public void testRunAs() {
assertThat(RemoteMonitoringAgentRole.INSTANCE.runAs().isEmpty(), is(true));
}
public void testUnauthorizedIndices() {
assertThat(RemoteMonitoringAgentRole.INSTANCE.indices().allowedIndicesMatcher(SearchAction.NAME).test("foo"), is(false));
assertThat(RemoteMonitoringAgentRole.INSTANCE.indices().allowedIndicesMatcher(SearchAction.NAME).test(".reporting"), is(false));
assertThat(RemoteMonitoringAgentRole.INSTANCE.indices().allowedIndicesMatcher(SearchAction.NAME).test(".kibana"), is(false));
assertThat(RemoteMonitoringAgentRole.INSTANCE.indices().allowedIndicesMatcher("indices:foo")
.test(randomAsciiOfLengthBetween(8, 24)), is(false));
}
public void testKibanaIndices() {
testAllIndexAccess(".monitoring-" + randomAsciiOfLength(randomIntBetween(0, 13)));
testAllIndexAccess(".marvel-es-" + randomAsciiOfLength(randomIntBetween(0, 13)));
}
private void testAllIndexAccess(String index) {
assertThat(RemoteMonitoringAgentRole.INSTANCE.indices().allowedIndicesMatcher("indices:foo").test(index), is(true));
assertThat(RemoteMonitoringAgentRole.INSTANCE.indices().allowedIndicesMatcher("indices:bar").test(index), is(true));
assertThat(RemoteMonitoringAgentRole.INSTANCE.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(index), is(true));
assertThat(RemoteMonitoringAgentRole.INSTANCE.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(index), is(true));
assertThat(RemoteMonitoringAgentRole.INSTANCE.indices().allowedIndicesMatcher(IndexAction.NAME).test(index), is(true));
assertThat(RemoteMonitoringAgentRole.INSTANCE.indices().allowedIndicesMatcher(DeleteAction.NAME).test(index), is(true));
assertThat(RemoteMonitoringAgentRole.INSTANCE.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(index), is(true));
assertThat(RemoteMonitoringAgentRole.INSTANCE.indices().allowedIndicesMatcher(SearchAction.NAME).test(index), is(true));
assertThat(RemoteMonitoringAgentRole.INSTANCE.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true));
assertThat(RemoteMonitoringAgentRole.INSTANCE.indices().allowedIndicesMatcher(GetIndexAction.NAME).test(index), is(true));
}
}

View File

@ -1,74 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteAction;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsAction;
import org.elasticsearch.action.admin.cluster.state.ClusterStateAction;
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsAction;
import org.elasticsearch.action.admin.indices.create.CreateIndexAction;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexAction;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsAction;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
import org.elasticsearch.action.bulk.BulkAction;
import org.elasticsearch.action.delete.DeleteAction;
import org.elasticsearch.action.get.GetAction;
import org.elasticsearch.action.index.IndexAction;
import org.elasticsearch.action.search.SearchAction;
import org.elasticsearch.action.update.UpdateAction;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.transport.TransportRequest;
import org.elasticsearch.xpack.monitoring.action.MonitoringBulkAction;
import org.elasticsearch.xpack.security.authc.Authentication;
import static org.hamcrest.Matchers.is;
import static org.mockito.Mockito.mock;
/**
* Unit tests for the built in reporting user role
*/
public class ReportingUserRoleTests extends ESTestCase {
public void testCluster() {
final TransportRequest request = new TransportRequest.Empty();
final Authentication authentication = mock(Authentication.class);
assertThat(ReportingUserRole.INSTANCE.cluster().check(ClusterHealthAction.NAME, request, authentication), is(false));
assertThat(ReportingUserRole.INSTANCE.cluster().check(ClusterStateAction.NAME, request, authentication), is(false));
assertThat(ReportingUserRole.INSTANCE.cluster().check(ClusterStatsAction.NAME, request, authentication), is(false));
assertThat(ReportingUserRole.INSTANCE.cluster().check(PutIndexTemplateAction.NAME, request, authentication), is(false));
assertThat(ReportingUserRole.INSTANCE.cluster().check(ClusterRerouteAction.NAME, request, authentication), is(false));
assertThat(ReportingUserRole.INSTANCE.cluster().check(ClusterUpdateSettingsAction.NAME, request, authentication), is(false));
assertThat(ReportingUserRole.INSTANCE.cluster().check(MonitoringBulkAction.NAME, request, authentication), is(false));
}
public void testRunAs() {
assertThat(ReportingUserRole.INSTANCE.runAs().isEmpty(), is(true));
}
public void testUnauthorizedIndices() {
assertThat(ReportingUserRole.INSTANCE.indices().allowedIndicesMatcher(SearchAction.NAME).test("foo"), is(false));
assertThat(ReportingUserRole.INSTANCE.indices().allowedIndicesMatcher(SearchAction.NAME).test(".reporting"), is(false));
assertThat(ReportingUserRole.INSTANCE.indices().allowedIndicesMatcher(SearchAction.NAME).test(".kibana"), is(false));
assertThat(ReportingUserRole.INSTANCE.indices().allowedIndicesMatcher("indices:foo").test(randomAsciiOfLengthBetween(8, 24)),
is(false));
}
public void testReadWriteAccess() {
final String index = ".reporting-" + randomAsciiOfLength(randomIntBetween(0, 13));
assertThat(ReportingUserRole.INSTANCE.indices().allowedIndicesMatcher("indices:foo").test(index), is(false));
assertThat(ReportingUserRole.INSTANCE.indices().allowedIndicesMatcher("indices:bar").test(index), is(false));
assertThat(ReportingUserRole.INSTANCE.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(index), is(false));
assertThat(ReportingUserRole.INSTANCE.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(index), is(false));
assertThat(ReportingUserRole.INSTANCE.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(index), is(false));
assertThat(ReportingUserRole.INSTANCE.indices().allowedIndicesMatcher(SearchAction.NAME).test(index), is(true));
assertThat(ReportingUserRole.INSTANCE.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true));
assertThat(ReportingUserRole.INSTANCE.indices().allowedIndicesMatcher(IndexAction.NAME).test(index), is(true));
assertThat(ReportingUserRole.INSTANCE.indices().allowedIndicesMatcher(UpdateAction.NAME).test(index), is(true));
assertThat(ReportingUserRole.INSTANCE.indices().allowedIndicesMatcher(DeleteAction.NAME).test(index), is(true));
assertThat(ReportingUserRole.INSTANCE.indices().allowedIndicesMatcher(BulkAction.NAME).test(index), is(true));
}
}

View File

@ -1,90 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.permission;
import org.elasticsearch.Version;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsAction;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexAction;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsAction;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
import org.elasticsearch.action.index.IndexAction;
import org.elasticsearch.action.search.SearchAction;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.xpack.security.action.role.PutRoleAction;
import org.elasticsearch.xpack.security.action.user.PutUserAction;
import org.elasticsearch.xpack.security.authc.Authentication;
import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl.IndexAccessControl;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.test.ESTestCase;
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
*/
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, 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() {
final Settings indexSettings = Settings.builder().put("index.version.created", Version.CURRENT).build();
final MetaData metaData = new MetaData.Builder()
.put(new IndexMetaData.Builder("a1").settings(indexSettings).numberOfShards(1).numberOfReplicas(0).build(), true)
.put(new IndexMetaData.Builder("a2").settings(indexSettings).numberOfShards(1).numberOfReplicas(0).build(), true)
.put(new IndexMetaData.Builder("aaaaaa").settings(indexSettings).numberOfShards(1).numberOfReplicas(0).build(), true)
.put(new IndexMetaData.Builder("bbbbb").settings(indexSettings).numberOfShards(1).numberOfReplicas(0).build(), true)
.put(new IndexMetaData.Builder("b")
.settings(indexSettings)
.numberOfShards(1)
.numberOfReplicas(0)
.putAlias(new AliasMetaData.Builder("ab").build())
.putAlias(new AliasMetaData.Builder("ba").build())
.build(), true)
.build();
Map<String, IndexAccessControl> authzMap =
SuperuserRole.INSTANCE.indices().authorize(SearchAction.NAME, Sets.newHashSet("a1", "ba"), metaData);
assertThat(authzMap.get("a1").isGranted(), is(true));
assertThat(authzMap.get("b").isGranted(), is(true));
authzMap = SuperuserRole.INSTANCE.indices().authorize(DeleteIndexAction.NAME, Sets.newHashSet("a1", "ba"), metaData);
assertThat(authzMap.get("a1").isGranted(), is(true));
assertThat(authzMap.get("b").isGranted(), is(true));
authzMap = SuperuserRole.INSTANCE.indices().authorize(IndexAction.NAME, Sets.newHashSet("a2", "ba"), metaData);
assertThat(authzMap.get("a2").isGranted(), is(true));
assertThat(authzMap.get("b").isGranted(), is(true));
authzMap = SuperuserRole.INSTANCE.indices().authorize(UpdateSettingsAction.NAME, Sets.newHashSet("aaaaaa", "ba"), metaData);
assertThat(authzMap.get("aaaaaa").isGranted(), is(true));
assertThat(authzMap.get("b").isGranted(), is(true));
assertTrue(SuperuserRole.INSTANCE.indices().check(SearchAction.NAME));
assertFalse(SuperuserRole.INSTANCE.indices().check("unknown"));
}
public void testRunAs() {
assertThat(SuperuserRole.INSTANCE.runAs().check(randomAsciiOfLengthBetween(1, 30)), is(true));
}
}

View File

@ -5,20 +5,16 @@
*/
package org.elasticsearch.xpack.security.authz.privilege;
import org.elasticsearch.action.ingest.DeletePipelineAction;
import org.elasticsearch.action.ingest.GetPipelineAction;
import org.elasticsearch.action.ingest.PutPipelineAction;
import org.elasticsearch.action.ingest.SimulatePipelineAction;
import org.elasticsearch.xpack.security.support.AutomatonPredicate;
import org.apache.lucene.util.automaton.Operations;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.xpack.security.support.Automatons;
import org.elasticsearch.test.ESTestCase;
import org.junit.Rule;
import org.junit.rules.ExpectedException;
import java.util.Set;
import java.util.function.Predicate;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
@ -26,24 +22,8 @@ public class PrivilegeTests extends ESTestCase {
@Rule
public ExpectedException thrown = ExpectedException.none();
public void testName() throws Exception {
Privilege.Name name12 = new Privilege.Name("name1", "name2");
Privilege.Name name34 = new Privilege.Name("name3", "name4");
Privilege.Name name1234 = randomBoolean() ? name12.add(name34) : name34.add(name12);
assertThat(name1234, equalTo(new Privilege.Name("name1", "name2", "name3", "name4")));
Privilege.Name name1 = name12.remove(new Privilege.Name("name2"));
assertThat(name1, equalTo(new Privilege.Name("name1")));
Privilege.Name name = name1.remove(new Privilege.Name("name1"));
assertThat(name, is(Privilege.Name.NONE));
Privilege.Name none = new Privilege.Name("name1", "name2", "none").remove(name12);
assertThat(none, is(Privilege.Name.NONE));
}
public void testSubActionPattern() throws Exception {
AutomatonPredicate predicate = new AutomatonPredicate(Automatons.patterns("foo*"));
Predicate<String> predicate = Automatons.predicate("foo*");
assertThat(predicate.test("foo[n][nodes]"), is(true));
assertThat(predicate.test("foo[n]"), is(true));
assertThat(predicate.test("bar[n][nodes]"), is(false));
@ -51,36 +31,36 @@ public class PrivilegeTests extends ESTestCase {
}
public void testCluster() throws Exception {
Privilege.Name name = new Privilege.Name("monitor");
Set<String> name = Sets.newHashSet("monitor");
ClusterPrivilege cluster = ClusterPrivilege.get(name);
assertThat(cluster, is(ClusterPrivilege.MONITOR));
// since "all" implies "monitor", this should collapse to All
name = new Privilege.Name("monitor", "all");
// since "all" implies "monitor", this should be the same language as All
name = Sets.newHashSet("monitor", "all");
cluster = ClusterPrivilege.get(name);
assertThat(cluster, is(ClusterPrivilege.ALL));
assertTrue(Operations.sameLanguage(ClusterPrivilege.ALL.automaton, cluster.automaton));
name = new Privilege.Name("monitor", "none");
name = Sets.newHashSet("monitor", "none");
cluster = ClusterPrivilege.get(name);
assertThat(cluster, is(ClusterPrivilege.MONITOR));
assertTrue(Operations.sameLanguage(ClusterPrivilege.MONITOR.automaton, cluster.automaton));
Privilege.Name name2 = new Privilege.Name("none", "monitor");
Set<String> name2 = Sets.newHashSet("none", "monitor");
ClusterPrivilege cluster2 = ClusterPrivilege.get(name2);
assertThat(cluster, is(cluster2));
}
public void testClusterTemplateActions() throws Exception {
Privilege.Name name = new Privilege.Name("indices:admin/template/delete");
Set<String> name = Sets.newHashSet("indices:admin/template/delete");
ClusterPrivilege cluster = ClusterPrivilege.get(name);
assertThat(cluster, notNullValue());
assertThat(cluster.predicate().test("indices:admin/template/delete"), is(true));
name = new Privilege.Name("indices:admin/template/get");
name = Sets.newHashSet("indices:admin/template/get");
cluster = ClusterPrivilege.get(name);
assertThat(cluster, notNullValue());
assertThat(cluster.predicate().test("indices:admin/template/get"), is(true));
name = new Privilege.Name("indices:admin/template/put");
name = Sets.newHashSet("indices:admin/template/put");
cluster = ClusterPrivilege.get(name);
assertThat(cluster, notNullValue());
assertThat(cluster.predicate().test("indices:admin/template/put"), is(true));
@ -88,56 +68,20 @@ public class PrivilegeTests extends ESTestCase {
public void testClusterInvalidName() throws Exception {
thrown.expect(IllegalArgumentException.class);
Privilege.Name actionName = new Privilege.Name("foobar");
Set<String> actionName = Sets.newHashSet("foobar");
ClusterPrivilege.get(actionName);
}
public void testClusterAction() throws Exception {
Privilege.Name actionName = new Privilege.Name("cluster:admin/snapshot/delete");
Set<String> actionName = Sets.newHashSet("cluster:admin/snapshot/delete");
ClusterPrivilege cluster = ClusterPrivilege.get(actionName);
assertThat(cluster, notNullValue());
assertThat(cluster.predicate().test("cluster:admin/snapshot/delete"), is(true));
assertThat(cluster.predicate().test("cluster:admin/snapshot/dele"), is(false));
}
public void testClusterAddCustom() throws Exception {
ClusterPrivilege.addCustom("foo", "cluster:bar");
boolean found = false;
for (ClusterPrivilege cluster : ClusterPrivilege.values()) {
if ("foo".equals(cluster.name.toString())) {
found = true;
assertThat(cluster.predicate().test("cluster:bar"), is(true));
}
}
assertThat(found, is(true));
ClusterPrivilege cluster = ClusterPrivilege.get(new Privilege.Name("foo"));
assertThat(cluster, notNullValue());
assertThat(cluster.name().toString(), is("foo"));
assertThat(cluster.predicate().test("cluster:bar"), is(true));
}
public void testClusterAddCustomInvalidPattern() throws Exception {
try {
ClusterPrivilege.addCustom("foo", "bar");
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("cannot register custom cluster privilege [foo]"));
assertThat(e.getMessage(), containsString("must follow the 'cluster:*' format"));
}
}
public void testClusterAddCustomAlreadyExists() throws Exception {
try {
ClusterPrivilege.addCustom("all", "bar");
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("cannot register custom cluster privilege [all]"));
assertThat(e.getMessage(), containsString("must follow the 'cluster:*' format"));
}
}
public void testIndexAction() throws Exception {
Privilege.Name actionName = new Privilege.Name("indices:admin/mapping/delete");
Set<String> actionName = Sets.newHashSet("indices:admin/mapping/delete");
IndexPrivilege index = IndexPrivilege.get(actionName);
assertThat(index, notNullValue());
assertThat(index.predicate().test("indices:admin/mapping/delete"), is(true));
@ -145,87 +89,20 @@ public class PrivilegeTests extends ESTestCase {
}
public void testIndexCollapse() throws Exception {
IndexPrivilege[] values = IndexPrivilege.values().toArray(new IndexPrivilege[IndexPrivilege.values().size()]);
IndexPrivilege[] values = IndexPrivilege.values().values().toArray(new IndexPrivilege[IndexPrivilege.values().size()]);
IndexPrivilege first = values[randomIntBetween(0, values.length-1)];
IndexPrivilege second = values[randomIntBetween(0, values.length-1)];
Privilege.Name name = new Privilege.Name(first.name().toString(), second.name().toString());
Set<String> name = Sets.newHashSet(first.name().iterator().next(), second.name().iterator().next());
IndexPrivilege index = IndexPrivilege.get(name);
if (first.implies(second)) {
assertThat(index, is(first));
}
if (second.implies(first)) {
assertThat(index, is(second));
}
}
public void testIndexImplies() throws Exception {
IndexPrivilege[] values = IndexPrivilege.values().toArray(new IndexPrivilege[IndexPrivilege.values().size()]);
IndexPrivilege first = values[randomIntBetween(0, values.length-1)];
IndexPrivilege second = values[randomIntBetween(0, values.length-1)];
Privilege.Name name = new Privilege.Name(first.name().toString(), second.name().toString());
IndexPrivilege index = IndexPrivilege.get(name);
assertThat(index.implies(first), is(true));
assertThat(index.implies(second), is(true));
if (first.implies(second)) {
assertThat(index, is(first));
}
if (second.implies(first)) {
if (index != second) {
IndexPrivilege idx = IndexPrivilege.get(name);
idx.name().toString();
}
assertThat(index, is(second));
}
for (IndexPrivilege other : IndexPrivilege.values()) {
if (first.implies(other) || second.implies(other) || index.isAlias(other)) {
assertThat("index privilege [" + index + "] should imply [" + other + "]", index.implies(other), is(true));
} else if (other.implies(first) && other.implies(second)) {
assertThat("index privilege [" + index + "] should not imply [" + other + "]", index.implies(other), is(false));
}
}
}
public void testIndexAddCustom() throws Exception {
IndexPrivilege.addCustom("foo", "indices:bar");
boolean found = false;
for (IndexPrivilege index : IndexPrivilege.values()) {
if ("foo".equals(index.name.toString())) {
found = true;
assertThat(index.predicate().test("indices:bar"), is(true));
}
}
assertThat(found, is(true));
IndexPrivilege index = IndexPrivilege.get(new Privilege.Name("foo"));
assertThat(index, notNullValue());
assertThat(index.name().toString(), is("foo"));
assertThat(index.predicate().test("indices:bar"), is(true));
}
public void testIndexAddCustomInvalidPattern() throws Exception {
try {
IndexPrivilege.addCustom("foo", "bar");
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("cannot register custom index privilege [foo]"));
assertThat(e.getMessage(), containsString("must follow the 'indices:*' format"));
}
}
public void testIndexAddCustomAlreadyExists() throws Exception {
try {
IndexPrivilege.addCustom("all", "bar");
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("cannot register custom index privilege [all]"));
assertThat(e.getMessage(), containsString("must follow the 'indices:*' format"));
if (Operations.subsetOf(second.getAutomaton(), first.getAutomaton())) {
assertTrue(Operations.sameLanguage(index.getAutomaton(), first.getAutomaton()));
} else if (Operations.subsetOf(first.getAutomaton(), second.getAutomaton())) {
assertTrue(Operations.sameLanguage(index.getAutomaton(), second.getAutomaton()));
} else {
assertFalse(Operations.sameLanguage(index.getAutomaton(), first.getAutomaton()));
assertFalse(Operations.sameLanguage(index.getAutomaton(), second.getAutomaton()));
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.security.authz.store;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.support.PlainActionFuture;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
import org.elasticsearch.xpack.security.authz.permission.Role;
import java.util.Collections;
import java.util.Set;
import static org.elasticsearch.mock.orig.Mockito.times;
import static org.elasticsearch.mock.orig.Mockito.verifyNoMoreInteractions;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anySetOf;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class CompositeRolesStoreTests extends ESTestCase {
public void testNegativeLookupsAreCached() {
final FileRolesStore fileRolesStore = mock(FileRolesStore.class);
when(fileRolesStore.roleDescriptors(anySetOf(String.class))).thenReturn(Collections.emptySet());
final NativeRolesStore nativeRolesStore = mock(NativeRolesStore.class);
doAnswer((invocationOnMock) -> {
ActionListener<Set<RoleDescriptor>> callback = (ActionListener<Set<RoleDescriptor>>) invocationOnMock.getArguments()[1];
callback.onResponse(Collections.emptySet());
return null;
}).when(nativeRolesStore).getRoleDescriptors(isA(String[].class), any(ActionListener.class));
final ReservedRolesStore reservedRolesStore = spy(new ReservedRolesStore());
final CompositeRolesStore compositeRolesStore =
new CompositeRolesStore(Settings.EMPTY, fileRolesStore, nativeRolesStore, reservedRolesStore);
verify(fileRolesStore).addListener(any(Runnable.class)); // adds a listener in ctor
final String roleName = randomAsciiOfLengthBetween(1, 10);
PlainActionFuture<Role> future = new PlainActionFuture<>();
final FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY);
compositeRolesStore.roles(Collections.singleton(roleName), fieldPermissionsCache, future);
final Role role = future.actionGet();
assertEquals(Role.EMPTY, role);
verify(reservedRolesStore).roleDescriptors();
verify(fileRolesStore).roleDescriptors(eq(Collections.singleton(roleName)));
verify(nativeRolesStore).getRoleDescriptors(isA(String[].class), any(ActionListener.class));
final int numberOfTimesToCall = scaledRandomIntBetween(0, 32);
final boolean getSuperuserRole = randomBoolean();
final Set<String> names = getSuperuserRole ? Sets.newHashSet(roleName, ReservedRolesStore.SUPERUSER_ROLE.name()) :
Collections.singleton(roleName);
for (int i = 0; i < numberOfTimesToCall; i++) {
future = new PlainActionFuture<>();
compositeRolesStore.roles(names, fieldPermissionsCache, future);
future.actionGet();
}
if (getSuperuserRole) {
// the superuser role was requested so we get the role descriptors again
verify(reservedRolesStore, times(2)).roleDescriptors();
}
verifyNoMoreInteractions(fileRolesStore, reservedRolesStore, nativeRolesStore);
}
}

View File

@ -7,6 +7,8 @@ package org.elasticsearch.xpack.security.authz.store;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.Logger;
import org.apache.lucene.util.automaton.MinimizationOperations;
import org.apache.lucene.util.automaton.Operations;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.test.ESTestCase;
@ -39,6 +41,7 @@ import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.hamcrest.Matchers.arrayContaining;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.empty;
@ -55,13 +58,15 @@ public class FileRolesStoreTests extends ESTestCase {
public void testParseFile() throws Exception {
Path path = getDataPath("roles.yml");
Map<String, Role> roles = FileRolesStore.parseFile(path, logger, Settings.builder()
Map<String, RoleDescriptor> roles = FileRolesStore.parseFile(path, logger, Settings.builder()
.put(XPackSettings.DLS_FLS_ENABLED.getKey(), true)
.build());
assertThat(roles, notNullValue());
assertThat(roles.size(), is(9));
Role role = roles.get("role1");
RoleDescriptor descriptor = roles.get("role1");
assertNotNull(descriptor);
Role role = Role.builder(descriptor, null).build();
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role1"));
assertThat(role.cluster(), notNullValue());
@ -69,7 +74,7 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
assertThat(role.indices().groups().length, is(2));
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
assertThat(role.runAs(), is(RunAsPermission.NONE));
IndicesPermission.Group group = role.indices().groups()[0];
assertThat(group.indices(), notNullValue());
@ -84,10 +89,12 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(group.indices().length, is(1));
assertThat(group.indices()[0], equalTo("idx3"));
assertThat(group.privilege(), notNullValue());
assertThat(group.privilege().implies(IndexPrivilege.READ), is(true));
assertThat(group.privilege().implies(IndexPrivilege.WRITE),is(true));
assertTrue(Operations.subsetOf(IndexPrivilege.READ.getAutomaton(), group.privilege().getAutomaton()));
assertTrue(Operations.subsetOf(IndexPrivilege.WRITE.getAutomaton(), group.privilege().getAutomaton()));
role = roles.get("role1.ab");
descriptor = roles.get("role1.ab");
assertNotNull(descriptor);
role = Role.builder(descriptor, null).build();
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role1.ab"));
assertThat(role.cluster(), notNullValue());
@ -95,65 +102,77 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
assertThat(role.indices().groups().length, is(0));
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
assertThat(role.runAs(), is(RunAsPermission.NONE));
role = roles.get("role2");
descriptor = roles.get("role2");
assertNotNull(descriptor);
role = Role.builder(descriptor, null).build();
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role2"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster().privilege(), is(ClusterPrivilege.ALL)); // MONITOR is collapsed into ALL
assertTrue(Operations.sameLanguage(role.cluster().privilege().getAutomaton(), ClusterPrivilege.ALL.getAutomaton()));
assertThat(role.indices(), notNullValue());
assertThat(role.indices(), is(IndicesPermission.Core.NONE));
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
assertThat(role.indices(), is(IndicesPermission.NONE));
assertThat(role.runAs(), is(RunAsPermission.NONE));
role = roles.get("role3");
descriptor = roles.get("role3");
assertNotNull(descriptor);
role = Role.builder(descriptor, null).build();
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role3"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
assertThat(role.cluster(), is(ClusterPermission.NONE));
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
assertThat(role.indices().groups().length, is(1));
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
assertThat(role.runAs(), is(RunAsPermission.NONE));
group = role.indices().groups()[0];
assertThat(group.indices(), notNullValue());
assertThat(group.indices().length, is(1));
assertThat(group.indices()[0], equalTo("/.*_.*/"));
assertThat(group.privilege(), notNullValue());
assertThat(group.privilege().isAlias(IndexPrivilege.union(IndexPrivilege.READ, IndexPrivilege.WRITE)), is(true));
assertTrue(Operations.sameLanguage(group.privilege().getAutomaton(),
MinimizationOperations.minimize(Operations.union(IndexPrivilege.READ.getAutomaton(), IndexPrivilege.WRITE.getAutomaton()),
Operations.DEFAULT_MAX_DETERMINIZED_STATES)));
role = roles.get("role4");
assertThat(role, nullValue());
descriptor = roles.get("role4");
assertNull(descriptor);
role = roles.get("role_run_as");
descriptor = roles.get("role_run_as");
assertNotNull(descriptor);
role = Role.builder(descriptor, null).build();
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role_run_as"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
assertThat(role.indices(), is(IndicesPermission.Core.NONE));
assertThat(role.cluster(), is(ClusterPermission.NONE));
assertThat(role.indices(), is(IndicesPermission.NONE));
assertThat(role.runAs(), notNullValue());
assertThat(role.runAs().check("user1"), is(true));
assertThat(role.runAs().check("user2"), is(true));
assertThat(role.runAs().check("user" + randomIntBetween(3, 9)), is(false));
role = roles.get("role_run_as1");
descriptor = roles.get("role_run_as1");
assertNotNull(descriptor);
role = Role.builder(descriptor, null).build();
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role_run_as1"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
assertThat(role.indices(), is(IndicesPermission.Core.NONE));
assertThat(role.cluster(), is(ClusterPermission.NONE));
assertThat(role.indices(), is(IndicesPermission.NONE));
assertThat(role.runAs(), notNullValue());
assertThat(role.runAs().check("user1"), is(true));
assertThat(role.runAs().check("user2"), is(true));
assertThat(role.runAs().check("user" + randomIntBetween(3, 9)), is(false));
role = roles.get("role_fields");
descriptor = roles.get("role_fields");
assertNotNull(descriptor);
role = Role.builder(descriptor, null).build();
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role_fields"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
assertThat(role.cluster(), is(ClusterPermission.NONE));
assertThat(role.runAs(), is(RunAsPermission.NONE));
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
assertThat(role.indices().groups().length, is(1));
@ -163,17 +182,19 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(group.indices().length, is(1));
assertThat(group.indices()[0], equalTo("field_idx"));
assertThat(group.privilege(), notNullValue());
assertThat(group.privilege().isAlias(IndexPrivilege.READ), is(true));
assertTrue(Operations.sameLanguage(group.privilege().getAutomaton(), IndexPrivilege.READ.getAutomaton()));
assertTrue(group.getFieldPermissions().grantsAccessTo("foo"));
assertTrue(group.getFieldPermissions().grantsAccessTo("boo"));
assertTrue(group.getFieldPermissions().hasFieldLevelSecurity());
role = roles.get("role_query");
descriptor = roles.get("role_query");
assertNotNull(descriptor);
role = Role.builder(descriptor, null).build();
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role_query"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
assertThat(role.cluster(), is(ClusterPermission.NONE));
assertThat(role.runAs(), is(RunAsPermission.NONE));
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
assertThat(role.indices().groups().length, is(1));
@ -183,16 +204,18 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(group.indices().length, is(1));
assertThat(group.indices()[0], equalTo("query_idx"));
assertThat(group.privilege(), notNullValue());
assertThat(group.privilege().isAlias(IndexPrivilege.READ), is(true));
assertTrue(Operations.sameLanguage(group.privilege().getAutomaton(), IndexPrivilege.READ.getAutomaton()));
assertFalse(group.getFieldPermissions().hasFieldLevelSecurity());
assertThat(group.getQuery(), notNullValue());
role = roles.get("role_query_fields");
descriptor = roles.get("role_query_fields");
assertNotNull(descriptor);
role = Role.builder(descriptor, null).build();
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role_query_fields"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
assertThat(role.cluster(), is(ClusterPermission.NONE));
assertThat(role.runAs(), is(RunAsPermission.NONE));
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
assertThat(role.indices().groups().length, is(1));
@ -202,7 +225,7 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(group.indices().length, is(1));
assertThat(group.indices()[0], equalTo("query_fields_idx"));
assertThat(group.privilege(), notNullValue());
assertThat(group.privilege().isAlias(IndexPrivilege.READ), is(true));
assertTrue(Operations.sameLanguage(group.privilege().getAutomaton(), IndexPrivilege.READ.getAutomaton()));
assertTrue(group.getFieldPermissions().grantsAccessTo("foo"));
assertTrue(group.getFieldPermissions().grantsAccessTo("boo"));
assertTrue(group.getFieldPermissions().hasFieldLevelSecurity());
@ -212,7 +235,7 @@ public class FileRolesStoreTests extends ESTestCase {
public void testParseFileWithFLSAndDLSDisabled() throws Exception {
Path path = getDataPath("roles.yml");
Logger logger = CapturingLogger.newCapturingLogger(Level.ERROR);
Map<String, Role> roles = FileRolesStore.parseFile(path, logger, Settings.builder()
Map<String, RoleDescriptor> roles = FileRolesStore.parseFile(path, logger, Settings.builder()
.put(XPackSettings.DLS_FLS_ENABLED.getKey(), false)
.build());
assertThat(roles, notNullValue());
@ -241,7 +264,7 @@ public class FileRolesStoreTests extends ESTestCase {
public void testDefaultRolesFile() throws Exception {
// TODO we should add the config dir to the resources so we don't copy this stuff around...
Path path = getDataPath("default_roles.yml");
Map<String, Role> roles = FileRolesStore.parseFile(path, logger, Settings.EMPTY);
Map<String, RoleDescriptor> roles = FileRolesStore.parseFile(path, logger, Settings.EMPTY);
assertThat(roles, notNullValue());
assertThat(roles.size(), is(0));
}
@ -268,12 +291,13 @@ public class FileRolesStoreTests extends ESTestCase {
watcherService = new ResourceWatcherService(settings, threadPool);
final CountDownLatch latch = new CountDownLatch(1);
FileRolesStore store = new FileRolesStore(settings, env, watcherService, latch::countDown);
store.start();
Role role = store.role("role1");
assertThat(role, notNullValue());
role = store.role("role5");
assertThat(role, nullValue());
Set<RoleDescriptor> descriptors = store.roleDescriptors(Collections.singleton("role1"));
assertThat(descriptors, notNullValue());
assertEquals(1, descriptors.size());
descriptors = store.roleDescriptors(Collections.singleton("role5"));
assertThat(descriptors, notNullValue());
assertTrue(descriptors.isEmpty());
watcherService.start();
@ -290,11 +314,14 @@ public class FileRolesStoreTests extends ESTestCase {
fail("Waited too long for the updated file to be picked up");
}
role = store.role("role5");
descriptors = store.roleDescriptors(Collections.singleton("role5"));
assertThat(descriptors, notNullValue());
assertEquals(1, descriptors.size());
Role role = Role.builder(descriptors.iterator().next(), null).build();
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role5"));
assertThat(role.cluster().check("cluster:monitor/foo/bar", null, null), is(true));
assertThat(role.cluster().check("cluster:admin/foo/bar", null, null), is(false));
assertThat(role.cluster().check("cluster:monitor/foo/bar"), is(true));
assertThat(role.cluster().check("cluster:admin/foo/bar"), is(false));
} finally {
if (watcherService != null) {
@ -307,17 +334,19 @@ public class FileRolesStoreTests extends ESTestCase {
public void testThatEmptyFileDoesNotResultInLoop() throws Exception {
Path file = createTempFile();
Files.write(file, Collections.singletonList("#"), StandardCharsets.UTF_8);
Map<String, Role> roles = FileRolesStore.parseFile(file, logger, Settings.EMPTY);
Map<String, RoleDescriptor> roles = FileRolesStore.parseFile(file, logger, Settings.EMPTY);
assertThat(roles.keySet(), is(empty()));
}
public void testThatInvalidRoleDefinitions() throws Exception {
Path path = getDataPath("invalid_roles.yml");
Logger logger = CapturingLogger.newCapturingLogger(Level.ERROR);
Map<String, Role> roles = FileRolesStore.parseFile(path, logger, Settings.EMPTY);
Map<String, RoleDescriptor> roles = FileRolesStore.parseFile(path, logger, Settings.EMPTY);
assertThat(roles.size(), is(1));
assertThat(roles, hasKey("valid_role"));
Role role = roles.get("valid_role");
RoleDescriptor descriptor = roles.get("valid_role");
assertNotNull(descriptor);
Role role = Role.builder(descriptor, null).build();
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("valid_role"));
@ -354,7 +383,7 @@ public class FileRolesStoreTests extends ESTestCase {
Logger logger = CapturingLogger.newCapturingLogger(Level.INFO);
Path path = getDataPath("reserved_roles.yml");
Map<String, Role> roles = FileRolesStore.parseFile(path, logger, Settings.EMPTY);
Map<String, RoleDescriptor> roles = FileRolesStore.parseFile(path, logger, Settings.EMPTY);
assertThat(roles, notNullValue());
assertThat(roles.size(), is(1));
@ -387,7 +416,6 @@ public class FileRolesStoreTests extends ESTestCase {
.build();
Environment env = new Environment(settings);
FileRolesStore store = new FileRolesStore(settings, env, mock(ResourceWatcherService.class));
store.start();
Map<String, Object> usageStats = store.usageStats();
@ -404,7 +432,7 @@ public class FileRolesStoreTests extends ESTestCase {
RoleDescriptor role = FileRolesStore.parseRoleDescriptor(roleString, path, logger, true,
Settings.EMPTY);
RoleDescriptor.IndicesPrivileges indicesPrivileges = role.getIndicesPrivileges()[0];
assertTrue(indicesPrivileges.getFieldPermissions().grantsAccessTo("foo"));
assertTrue(indicesPrivileges.getFieldPermissions().grantsAccessTo("boo"));
assertThat(indicesPrivileges.getGrantedFields(), arrayContaining("foo", "boo"));
assertNull(indicesPrivileges.getDeniedFields());
}
}

View File

@ -5,30 +5,16 @@
*/
package org.elasticsearch.xpack.security.authz.store;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.support.PlainActionFuture;
import org.elasticsearch.cluster.ClusterChangedEvent;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.get.GetResult;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.xpack.security.InternalClient;
import org.elasticsearch.xpack.security.SecurityTemplateService;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.Role;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.concurrent.atomic.AtomicBoolean;
import static org.elasticsearch.xpack.security.test.SecurityTestUtils.getClusterStateWithSecurityIndex;
import static org.mockito.Mockito.mock;
import static org.hamcrest.Matchers.arrayContaining;
public class NativeRolesStoreTests extends ESTestCase {
@ -38,52 +24,10 @@ public class NativeRolesStoreTests extends ESTestCase {
byte[] bytes = Files.readAllBytes(path);
String roleString = new String(bytes, Charset.defaultCharset());
RoleDescriptor role = NativeRolesStore.transformRole("role1", new BytesArray(roleString), logger);
assertNotNull(role);
assertNotNull(role.getIndicesPrivileges());
RoleDescriptor.IndicesPrivileges indicesPrivileges = role.getIndicesPrivileges()[0];
assertTrue(indicesPrivileges.getFieldPermissions().grantsAccessTo("foo"));
assertTrue(indicesPrivileges.getFieldPermissions().grantsAccessTo("boo"));
}
public void testNegativeLookupsAreCached() {
final InternalClient internalClient = mock(InternalClient.class);
final AtomicBoolean methodCalled = new AtomicBoolean(false);
final NativeRolesStore rolesStore = new NativeRolesStore(Settings.EMPTY, internalClient) {
@Override
public State state() {
return State.STARTED;
}
@Override
void executeGetRoleRequest(String role, ActionListener<GetResponse> listener) {
if (methodCalled.compareAndSet(false, true)) {
listener.onResponse(new GetResponse(new GetResult(SecurityTemplateService.SECURITY_INDEX_NAME, "role",
role, -1, false, BytesArray.EMPTY, Collections.emptyMap())));
} else {
fail("method called more than once!");
}
}
};
// setup the roles store so the security index exists
rolesStore.clusterChanged(new ClusterChangedEvent("negative_lookups", getClusterStateWithSecurityIndex(), getEmptyClusterState()));
final String roleName = randomAsciiOfLengthBetween(1, 10);
PlainActionFuture<Role> future = new PlainActionFuture<>();
rolesStore.role(roleName, future);
Role role = future.actionGet();
assertTrue(methodCalled.get());
assertNull(role);
final int numberOfRetries = scaledRandomIntBetween(1, 20);
for (int i = 0; i < numberOfRetries; i++) {
future = new PlainActionFuture<>();
rolesStore.role(roleName, future);
role = future.actionGet();
assertTrue(methodCalled.get());
assertNull(role);
}
}
private ClusterState getEmptyClusterState() {
return ClusterState.builder(new ClusterName(NativeRolesStoreTests.class.getName())).build();
assertThat(indicesPrivileges.getGrantedFields(), arrayContaining("foo", "boo"));
assertNull(indicesPrivileges.getDeniedFields());
}
}

View File

@ -5,91 +5,338 @@
*/
package org.elasticsearch.xpack.security.authz.store;
import org.elasticsearch.xpack.security.SecurityContext;
import org.elasticsearch.xpack.security.authz.permission.IngestAdminRole;
import org.elasticsearch.xpack.security.authz.permission.KibanaRole;
import org.elasticsearch.xpack.security.authz.permission.KibanaUserRole;
import org.elasticsearch.xpack.security.authz.permission.LogstashSystemRole;
import org.elasticsearch.xpack.security.authz.permission.MonitoringUserRole;
import org.elasticsearch.xpack.security.authz.permission.RemoteMonitoringAgentRole;
import org.elasticsearch.xpack.security.authz.permission.ReportingUserRole;
import org.elasticsearch.xpack.security.authz.permission.SuperuserRole;
import org.elasticsearch.xpack.security.authz.permission.TransportClientRole;
import org.elasticsearch.xpack.security.user.ElasticUser;
import org.elasticsearch.xpack.security.user.KibanaUser;
import org.elasticsearch.Version;
import org.elasticsearch.action.admin.cluster.health.ClusterHealthAction;
import org.elasticsearch.action.admin.cluster.reroute.ClusterRerouteAction;
import org.elasticsearch.action.admin.cluster.settings.ClusterUpdateSettingsAction;
import org.elasticsearch.action.admin.cluster.state.ClusterStateAction;
import org.elasticsearch.action.admin.cluster.stats.ClusterStatsAction;
import org.elasticsearch.action.admin.indices.create.CreateIndexAction;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexAction;
import org.elasticsearch.action.admin.indices.get.GetIndexAction;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsAction;
import org.elasticsearch.action.admin.indices.template.delete.DeleteIndexTemplateAction;
import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesAction;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
import org.elasticsearch.action.bulk.BulkAction;
import org.elasticsearch.action.delete.DeleteAction;
import org.elasticsearch.action.get.GetAction;
import org.elasticsearch.action.index.IndexAction;
import org.elasticsearch.action.ingest.DeletePipelineAction;
import org.elasticsearch.action.ingest.GetPipelineAction;
import org.elasticsearch.action.ingest.PutPipelineAction;
import org.elasticsearch.action.search.MultiSearchAction;
import org.elasticsearch.action.search.SearchAction;
import org.elasticsearch.action.update.UpdateAction;
import org.elasticsearch.cluster.metadata.AliasMetaData;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.xpack.monitoring.action.MonitoringBulkAction;
import org.elasticsearch.xpack.security.action.role.PutRoleAction;
import org.elasticsearch.xpack.security.action.user.PutUserAction;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.accesscontrol.IndicesAccessControl.IndexAccessControl;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissionsCache;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.test.ESTestCase;
import org.junit.Before;
import static org.hamcrest.Matchers.contains;
import java.util.Arrays;
import java.util.Map;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Unit tests for the {@link ReservedRolesStore}
*/
public class ReservedRolesStoreTests extends ESTestCase {
private final User user = new User("joe");
private SecurityContext securityContext;
private ReservedRolesStore reservedRolesStore;
@Before
public void setupMocks() {
securityContext = mock(SecurityContext.class);
when(securityContext.getUser()).thenReturn(user);
reservedRolesStore = new ReservedRolesStore();
}
public void testRetrievingReservedRoles() {
assertThat(reservedRolesStore.role(SuperuserRole.NAME), sameInstance(SuperuserRole.INSTANCE));
assertThat(reservedRolesStore.roleDescriptor(SuperuserRole.NAME), sameInstance(SuperuserRole.DESCRIPTOR));
assertThat(reservedRolesStore.role(TransportClientRole.NAME), sameInstance(TransportClientRole.INSTANCE));
assertThat(reservedRolesStore.roleDescriptor(TransportClientRole.NAME), sameInstance(TransportClientRole.DESCRIPTOR));
assertThat(reservedRolesStore.role(KibanaUserRole.NAME), sameInstance(KibanaUserRole.INSTANCE));
assertThat(reservedRolesStore.roleDescriptor(KibanaUserRole.NAME), sameInstance(KibanaUserRole.DESCRIPTOR));
assertThat(reservedRolesStore.role(KibanaRole.NAME), sameInstance(KibanaRole.INSTANCE));
assertThat(reservedRolesStore.roleDescriptor(KibanaRole.NAME), sameInstance(KibanaRole.DESCRIPTOR));
assertThat(reservedRolesStore.role(IngestAdminRole.NAME), sameInstance(IngestAdminRole.INSTANCE));
assertThat(reservedRolesStore.roleDescriptor(IngestAdminRole.NAME), sameInstance(IngestAdminRole.DESCRIPTOR));
assertThat(reservedRolesStore.role(MonitoringUserRole.NAME), sameInstance(MonitoringUserRole.INSTANCE));
assertThat(reservedRolesStore.roleDescriptor(MonitoringUserRole.NAME), sameInstance(MonitoringUserRole.DESCRIPTOR));
assertThat(reservedRolesStore.role(RemoteMonitoringAgentRole.NAME), sameInstance(RemoteMonitoringAgentRole.INSTANCE));
assertThat(reservedRolesStore.roleDescriptor(RemoteMonitoringAgentRole.NAME), sameInstance(RemoteMonitoringAgentRole.DESCRIPTOR));
assertThat(reservedRolesStore.role(ReportingUserRole.NAME), sameInstance(ReportingUserRole.INSTANCE));
assertThat(reservedRolesStore.roleDescriptor(ReportingUserRole.NAME), sameInstance(ReportingUserRole.DESCRIPTOR));
assertThat(reservedRolesStore.role(LogstashSystemRole.NAME), sameInstance(LogstashSystemRole.INSTANCE));
assertThat(reservedRolesStore.roleDescriptor(LogstashSystemRole.NAME), sameInstance(LogstashSystemRole.DESCRIPTOR));
assertThat(reservedRolesStore.roleDescriptors(), contains(SuperuserRole.DESCRIPTOR, TransportClientRole.DESCRIPTOR,
KibanaUserRole.DESCRIPTOR, KibanaRole.DESCRIPTOR, MonitoringUserRole.DESCRIPTOR, RemoteMonitoringAgentRole.DESCRIPTOR,
IngestAdminRole.DESCRIPTOR, ReportingUserRole.DESCRIPTOR, LogstashSystemRole.DESCRIPTOR));
assertThat(reservedRolesStore.role(SystemUser.ROLE_NAME), nullValue());
}
public void testIsReserved() {
assertThat(ReservedRolesStore.isReserved(KibanaRole.NAME), is(true));
assertThat(ReservedRolesStore.isReserved(SuperuserRole.NAME), is(true));
assertThat(ReservedRolesStore.isReserved("kibana"), is(true));
assertThat(ReservedRolesStore.isReserved("superuser"), is(true));
assertThat(ReservedRolesStore.isReserved("foobar"), is(false));
assertThat(ReservedRolesStore.isReserved(SystemUser.ROLE_NAME), is(true));
assertThat(ReservedRolesStore.isReserved(TransportClientRole.NAME), is(true));
assertThat(ReservedRolesStore.isReserved(KibanaUserRole.NAME), is(true));
assertThat(ReservedRolesStore.isReserved(IngestAdminRole.NAME), is(true));
assertThat(ReservedRolesStore.isReserved(RemoteMonitoringAgentRole.NAME), is(true));
assertThat(ReservedRolesStore.isReserved(MonitoringUserRole.NAME), is(true));
assertThat(ReservedRolesStore.isReserved(ReportingUserRole.NAME), is(true));
assertThat(ReservedRolesStore.isReserved("transport_client"), is(true));
assertThat(ReservedRolesStore.isReserved("kibana_user"), is(true));
assertThat(ReservedRolesStore.isReserved("ingest_admin"), is(true));
assertThat(ReservedRolesStore.isReserved("remote_monitoring_agent"), is(true));
assertThat(ReservedRolesStore.isReserved("monitoring_user"), is(true));
assertThat(ReservedRolesStore.isReserved("reporting_user"), is(true));
}
public void testIngestAdminRole() {
RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("ingest_admin");
assertNotNull(roleDescriptor);
assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
Role ingestAdminRole = Role.builder(roleDescriptor, null).build();
assertThat(ingestAdminRole.cluster().check(PutIndexTemplateAction.NAME), is(true));
assertThat(ingestAdminRole.cluster().check(GetIndexTemplatesAction.NAME), is(true));
assertThat(ingestAdminRole.cluster().check(DeleteIndexTemplateAction.NAME), is(true));
assertThat(ingestAdminRole.cluster().check(PutPipelineAction.NAME), is(true));
assertThat(ingestAdminRole.cluster().check(GetPipelineAction.NAME), is(true));
assertThat(ingestAdminRole.cluster().check(DeletePipelineAction.NAME), is(true));
assertThat(ingestAdminRole.cluster().check(ClusterRerouteAction.NAME), is(false));
assertThat(ingestAdminRole.cluster().check(ClusterUpdateSettingsAction.NAME), is(false));
assertThat(ingestAdminRole.cluster().check(MonitoringBulkAction.NAME), is(false));
assertThat(ingestAdminRole.indices().allowedIndicesMatcher(IndexAction.NAME).test("foo"), is(false));
assertThat(ingestAdminRole.indices().allowedIndicesMatcher("indices:foo").test(randomAsciiOfLengthBetween(8, 24)),
is(false));
assertThat(ingestAdminRole.indices().allowedIndicesMatcher(GetAction.NAME).test(randomAsciiOfLengthBetween(8, 24)),
is(false));
}
public void testKibanaRole() {
RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("kibana");
assertNotNull(roleDescriptor);
assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
Role kibanaRole = Role.builder(roleDescriptor, null).build();
assertThat(kibanaRole.cluster().check(ClusterHealthAction.NAME), is(true));
assertThat(kibanaRole.cluster().check(ClusterStateAction.NAME), is(true));
assertThat(kibanaRole.cluster().check(ClusterStatsAction.NAME), is(true));
assertThat(kibanaRole.cluster().check(PutIndexTemplateAction.NAME), is(false));
assertThat(kibanaRole.cluster().check(ClusterRerouteAction.NAME), is(false));
assertThat(kibanaRole.cluster().check(ClusterUpdateSettingsAction.NAME), is(false));
assertThat(kibanaRole.cluster().check(MonitoringBulkAction.NAME), is(true));
assertThat(kibanaRole.runAs().check(randomAsciiOfLengthBetween(1, 12)), is(false));
assertThat(kibanaRole.indices().allowedIndicesMatcher(IndexAction.NAME).test("foo"), is(false));
assertThat(kibanaRole.indices().allowedIndicesMatcher(IndexAction.NAME).test(".reporting"), is(false));
assertThat(kibanaRole.indices().allowedIndicesMatcher("indices:foo").test(randomAsciiOfLengthBetween(8, 24)), is(false));
Arrays.asList(".kibana", ".kibana-devnull", ".reporting-" + randomAsciiOfLength(randomIntBetween(0, 13))).forEach((index) -> {
logger.info("index name [{}]", index);
assertThat(kibanaRole.indices().allowedIndicesMatcher("indices:foo").test(index), is(true));
assertThat(kibanaRole.indices().allowedIndicesMatcher("indices:bar").test(index), is(true));
assertThat(kibanaRole.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(index), is(true));
assertThat(kibanaRole.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(index), is(true));
assertThat(kibanaRole.indices().allowedIndicesMatcher(IndexAction.NAME).test(index), is(true));
assertThat(kibanaRole.indices().allowedIndicesMatcher(DeleteAction.NAME).test(index), is(true));
assertThat(kibanaRole.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(index), is(true));
});
}
public void testKibanaUserRole() {
RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("kibana_user");
assertNotNull(roleDescriptor);
assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
Role kibanaUserRole = Role.builder(roleDescriptor, null).build();
assertThat(kibanaUserRole.cluster().check(ClusterHealthAction.NAME), is(true));
assertThat(kibanaUserRole.cluster().check(ClusterStateAction.NAME), is(true));
assertThat(kibanaUserRole.cluster().check(ClusterStatsAction.NAME), is(true));
assertThat(kibanaUserRole.cluster().check(PutIndexTemplateAction.NAME), is(false));
assertThat(kibanaUserRole.cluster().check(ClusterRerouteAction.NAME), is(false));
assertThat(kibanaUserRole.cluster().check(ClusterUpdateSettingsAction.NAME), is(false));
assertThat(kibanaUserRole.cluster().check(MonitoringBulkAction.NAME), is(false));
assertThat(kibanaUserRole.runAs().check(randomAsciiOfLengthBetween(1, 12)), is(false));
assertThat(kibanaUserRole.indices().allowedIndicesMatcher(IndexAction.NAME).test("foo"), is(false));
assertThat(kibanaUserRole.indices().allowedIndicesMatcher(IndexAction.NAME).test(".reporting"), is(false));
assertThat(kibanaUserRole.indices().allowedIndicesMatcher("indices:foo")
.test(randomAsciiOfLengthBetween(8, 24)), is(false));
Arrays.asList(".kibana", ".kibana-devnull").forEach((index) -> {
logger.info("index name [{}]", index);
assertThat(kibanaUserRole.indices().allowedIndicesMatcher("indices:foo").test(index), is(false));
assertThat(kibanaUserRole.indices().allowedIndicesMatcher("indices:bar").test(index), is(false));
assertThat(kibanaUserRole.indices().allowedIndicesMatcher(DeleteAction.NAME).test(index), is(true));
assertThat(kibanaUserRole.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(index), is(true));
assertThat(kibanaUserRole.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(index), is(true));
assertThat(kibanaUserRole.indices().allowedIndicesMatcher(IndexAction.NAME).test(index), is(true));
assertThat(kibanaUserRole.indices().allowedIndicesMatcher(SearchAction.NAME).test(index), is(true));
assertThat(kibanaUserRole.indices().allowedIndicesMatcher(MultiSearchAction.NAME).test(index), is(true));
assertThat(kibanaUserRole.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(index), is(true));
});
}
public void testMonitoringUserRole() {
RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("monitoring_user");
assertNotNull(roleDescriptor);
assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
Role monitoringUserRole = Role.builder(roleDescriptor, null).build();
assertThat(monitoringUserRole.cluster().check(ClusterHealthAction.NAME), is(false));
assertThat(monitoringUserRole.cluster().check(ClusterStateAction.NAME), is(false));
assertThat(monitoringUserRole.cluster().check(ClusterStatsAction.NAME), is(false));
assertThat(monitoringUserRole.cluster().check(PutIndexTemplateAction.NAME), is(false));
assertThat(monitoringUserRole.cluster().check(ClusterRerouteAction.NAME), is(false));
assertThat(monitoringUserRole.cluster().check(ClusterUpdateSettingsAction.NAME), is(false));
assertThat(monitoringUserRole.cluster().check(MonitoringBulkAction.NAME), is(false));
assertThat(monitoringUserRole.runAs().check(randomAsciiOfLengthBetween(1, 12)), is(false));
assertThat(monitoringUserRole.indices().allowedIndicesMatcher(SearchAction.NAME).test("foo"), is(false));
assertThat(monitoringUserRole.indices().allowedIndicesMatcher(SearchAction.NAME).test(".reporting"), is(false));
assertThat(monitoringUserRole.indices().allowedIndicesMatcher(SearchAction.NAME).test(".kibana"), is(false));
assertThat(monitoringUserRole.indices().allowedIndicesMatcher("indices:foo").test(randomAsciiOfLengthBetween(8, 24)),
is(false));
Arrays.asList(".monitoring-" + randomAsciiOfLength(randomIntBetween(0, 13)),
".marvel-es-" + randomAsciiOfLength(randomIntBetween(0, 13))).forEach((index) -> {
assertThat(monitoringUserRole.indices().allowedIndicesMatcher("indices:foo").test(index), is(false));
assertThat(monitoringUserRole.indices().allowedIndicesMatcher("indices:bar").test(index), is(false));
assertThat(monitoringUserRole.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(index), is(false));
assertThat(monitoringUserRole.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(index), is(false));
assertThat(monitoringUserRole.indices().allowedIndicesMatcher(IndexAction.NAME).test(index), is(false));
assertThat(monitoringUserRole.indices().allowedIndicesMatcher(DeleteAction.NAME).test(index), is(false));
assertThat(monitoringUserRole.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(index), is(false));
assertThat(monitoringUserRole.indices().allowedIndicesMatcher(SearchAction.NAME).test(index), is(true));
assertThat(monitoringUserRole.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true));
});
}
public void testRemoteMonitoringAgentRole() {
RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("remote_monitoring_agent");
assertNotNull(roleDescriptor);
assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
Role remoteMonitoringAgentRole = Role.builder(roleDescriptor, null).build();
assertThat(remoteMonitoringAgentRole.cluster().check(ClusterHealthAction.NAME), is(true));
assertThat(remoteMonitoringAgentRole.cluster().check(ClusterStateAction.NAME), is(true));
assertThat(remoteMonitoringAgentRole.cluster().check(ClusterStatsAction.NAME), is(true));
assertThat(remoteMonitoringAgentRole.cluster().check(PutIndexTemplateAction.NAME), is(true));
assertThat(remoteMonitoringAgentRole.cluster().check(ClusterRerouteAction.NAME), is(false));
assertThat(remoteMonitoringAgentRole.cluster().check(ClusterUpdateSettingsAction.NAME),
is(false));
assertThat(remoteMonitoringAgentRole.cluster().check(MonitoringBulkAction.NAME), is(false));
assertThat(remoteMonitoringAgentRole.runAs().check(randomAsciiOfLengthBetween(1, 12)), is(false));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(SearchAction.NAME).test("foo"), is(false));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(SearchAction.NAME).test(".reporting"), is(false));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(SearchAction.NAME).test(".kibana"), is(false));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher("indices:foo")
.test(randomAsciiOfLengthBetween(8, 24)), is(false));
Arrays.asList(".monitoring-" + randomAsciiOfLength(randomIntBetween(0, 13)),
".marvel-es-" + randomAsciiOfLength(randomIntBetween(0, 13))).forEach((index) -> {
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher("indices:foo").test(index), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher("indices:bar").test(index), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(index), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(index), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(IndexAction.NAME).test(index), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(DeleteAction.NAME).test(index), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(index), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(SearchAction.NAME).test(index), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true));
assertThat(remoteMonitoringAgentRole.indices().allowedIndicesMatcher(GetIndexAction.NAME).test(index), is(true));
});
}
public void testReportingUserRole() {
RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("reporting_user");
assertNotNull(roleDescriptor);
assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
Role reportingUserRole = Role.builder(roleDescriptor, null).build();
assertThat(reportingUserRole.cluster().check(ClusterHealthAction.NAME), is(false));
assertThat(reportingUserRole.cluster().check(ClusterStateAction.NAME), is(false));
assertThat(reportingUserRole.cluster().check(ClusterStatsAction.NAME), is(false));
assertThat(reportingUserRole.cluster().check(PutIndexTemplateAction.NAME), is(false));
assertThat(reportingUserRole.cluster().check(ClusterRerouteAction.NAME), is(false));
assertThat(reportingUserRole.cluster().check(ClusterUpdateSettingsAction.NAME), is(false));
assertThat(reportingUserRole.cluster().check(MonitoringBulkAction.NAME), is(false));
assertThat(reportingUserRole.runAs().check(randomAsciiOfLengthBetween(1, 12)), is(false));
assertThat(reportingUserRole.indices().allowedIndicesMatcher(SearchAction.NAME).test("foo"), is(false));
assertThat(reportingUserRole.indices().allowedIndicesMatcher(SearchAction.NAME).test(".reporting"), is(false));
assertThat(reportingUserRole.indices().allowedIndicesMatcher(SearchAction.NAME).test(".kibana"), is(false));
assertThat(reportingUserRole.indices().allowedIndicesMatcher("indices:foo").test(randomAsciiOfLengthBetween(8, 24)),
is(false));
final String index = ".reporting-" + randomAsciiOfLength(randomIntBetween(0, 13));
assertThat(reportingUserRole.indices().allowedIndicesMatcher("indices:foo").test(index), is(false));
assertThat(reportingUserRole.indices().allowedIndicesMatcher("indices:bar").test(index), is(false));
assertThat(reportingUserRole.indices().allowedIndicesMatcher(DeleteIndexAction.NAME).test(index), is(false));
assertThat(reportingUserRole.indices().allowedIndicesMatcher(CreateIndexAction.NAME).test(index), is(false));
assertThat(reportingUserRole.indices().allowedIndicesMatcher(UpdateSettingsAction.NAME).test(index), is(false));
assertThat(reportingUserRole.indices().allowedIndicesMatcher(SearchAction.NAME).test(index), is(true));
assertThat(reportingUserRole.indices().allowedIndicesMatcher(GetAction.NAME).test(index), is(true));
assertThat(reportingUserRole.indices().allowedIndicesMatcher(IndexAction.NAME).test(index), is(true));
assertThat(reportingUserRole.indices().allowedIndicesMatcher(UpdateAction.NAME).test(index), is(true));
assertThat(reportingUserRole.indices().allowedIndicesMatcher(DeleteAction.NAME).test(index), is(true));
assertThat(reportingUserRole.indices().allowedIndicesMatcher(BulkAction.NAME).test(index), is(true));
}
public void testSuperuserRole() {
RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("superuser");
assertNotNull(roleDescriptor);
assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
Role superuserRole = Role.builder(roleDescriptor, null).build();
assertThat(superuserRole.cluster().check(ClusterHealthAction.NAME), is(true));
assertThat(superuserRole.cluster().check(ClusterUpdateSettingsAction.NAME), is(true));
assertThat(superuserRole.cluster().check(PutUserAction.NAME), is(true));
assertThat(superuserRole.cluster().check(PutRoleAction.NAME), is(true));
assertThat(superuserRole.cluster().check(PutIndexTemplateAction.NAME), is(true));
assertThat(superuserRole.cluster().check("internal:admin/foo"), is(false));
final Settings indexSettings = Settings.builder().put("index.version.created", Version.CURRENT).build();
final MetaData metaData = new MetaData.Builder()
.put(new IndexMetaData.Builder("a1").settings(indexSettings).numberOfShards(1).numberOfReplicas(0).build(), true)
.put(new IndexMetaData.Builder("a2").settings(indexSettings).numberOfShards(1).numberOfReplicas(0).build(), true)
.put(new IndexMetaData.Builder("aaaaaa").settings(indexSettings).numberOfShards(1).numberOfReplicas(0).build(), true)
.put(new IndexMetaData.Builder("bbbbb").settings(indexSettings).numberOfShards(1).numberOfReplicas(0).build(), true)
.put(new IndexMetaData.Builder("b")
.settings(indexSettings)
.numberOfShards(1)
.numberOfReplicas(0)
.putAlias(new AliasMetaData.Builder("ab").build())
.putAlias(new AliasMetaData.Builder("ba").build())
.build(), true)
.build();
FieldPermissionsCache fieldPermissionsCache = new FieldPermissionsCache(Settings.EMPTY);
Map<String, IndexAccessControl> authzMap =
superuserRole.indices().authorize(SearchAction.NAME, Sets.newHashSet("a1", "ba"), metaData, 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);
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);
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);
assertThat(authzMap.get("aaaaaa").isGranted(), is(true));
assertThat(authzMap.get("b").isGranted(), is(true));
assertTrue(superuserRole.indices().check(SearchAction.NAME));
assertFalse(superuserRole.indices().check("unknown"));
assertThat(superuserRole.runAs().check(randomAsciiOfLengthBetween(1, 30)), is(true));
}
public void testLogstashSystemRole() {
RoleDescriptor roleDescriptor = new ReservedRolesStore().roleDescriptor("logstash_system");
assertNotNull(roleDescriptor);
assertThat(roleDescriptor.getMetadata(), hasEntry("_reserved", true));
Role logstashSystemRole = Role.builder(roleDescriptor, null).build();
assertThat(logstashSystemRole.cluster().check(ClusterHealthAction.NAME), is(true));
assertThat(logstashSystemRole.cluster().check(ClusterStateAction.NAME), is(true));
assertThat(logstashSystemRole.cluster().check(ClusterStatsAction.NAME), is(true));
assertThat(logstashSystemRole.cluster().check(PutIndexTemplateAction.NAME), is(false));
assertThat(logstashSystemRole.cluster().check(ClusterRerouteAction.NAME), is(false));
assertThat(logstashSystemRole.cluster().check(ClusterUpdateSettingsAction.NAME), is(false));
assertThat(logstashSystemRole.cluster().check(MonitoringBulkAction.NAME), is(true));
assertThat(logstashSystemRole.runAs().check(randomAsciiOfLengthBetween(1, 30)), is(false));
assertThat(logstashSystemRole.indices().allowedIndicesMatcher(IndexAction.NAME).test("foo"), is(false));
assertThat(logstashSystemRole.indices().allowedIndicesMatcher(IndexAction.NAME).test(".reporting"), is(false));
assertThat(logstashSystemRole.indices().allowedIndicesMatcher("indices:foo").test(randomAsciiOfLengthBetween(8, 24)),
is(false));
}
}

View File

@ -26,13 +26,12 @@ import org.elasticsearch.xpack.security.authc.Authentication.RealmRef;
import org.elasticsearch.xpack.security.authc.AuthenticationService;
import org.elasticsearch.xpack.security.authz.AuthorizationService;
import org.elasticsearch.xpack.security.authz.permission.Role;
import org.elasticsearch.xpack.security.authz.permission.SuperuserRole;
import org.elasticsearch.xpack.security.authz.store.ReservedRolesStore;
import org.elasticsearch.xpack.security.user.SystemUser;
import org.elasticsearch.xpack.security.user.User;
import org.elasticsearch.xpack.security.user.XPackUser;
import org.junit.Before;
import java.util.Collection;
import java.util.Collections;
import static org.elasticsearch.mock.orig.Mockito.times;
@ -85,7 +84,7 @@ public class ServerTransportFilterTests extends ESTestCase {
PlainActionFuture<Void> future = new PlainActionFuture<>();
filter.inbound("_action", request, channel, future);
//future.get(); // don't block it's not called really just mocked
verify(authzService).authorize(authentication, "_action", request, Collections.emptyList(), Collections.emptyList());
verify(authzService).authorize(authentication, "_action", request, null, null);
}
public void testInboundDestructiveOperations() throws Exception {
@ -108,7 +107,7 @@ public class ServerTransportFilterTests extends ESTestCase {
verify(listener).onFailure(isA(IllegalArgumentException.class));
verifyNoMoreInteractions(authzService);
} else {
verify(authzService).authorize(authentication, action, request, Collections.emptyList(), Collections.emptyList());
verify(authzService).authorize(authentication, action, request, null, null);
}
}
@ -143,24 +142,23 @@ public class ServerTransportFilterTests extends ESTestCase {
callback.onResponse(authentication);
return Void.TYPE;
}).when(authcService).authenticate(eq("_action"), eq(request), eq(null), any(ActionListener.class));
final Role empty = Role.EMPTY;
doAnswer((i) -> {
ActionListener callback =
(ActionListener) i.getArguments()[1];
callback.onResponse(Collections.emptyList());
callback.onResponse(empty);
return Void.TYPE;
}).when(authzService).roles(any(User.class), any(ActionListener.class));
when(authentication.getUser()).thenReturn(XPackUser.INSTANCE);
when(authentication.getRunAsUser()).thenReturn(XPackUser.INSTANCE);
PlainActionFuture<Void> future = new PlainActionFuture<>();
doThrow(authorizationError("authz failed")).when(authzService).authorize(authentication, "_action", request,
Collections.emptyList(), Collections.emptyList());
try {
empty, null);
ElasticsearchSecurityException e = expectThrows(ElasticsearchSecurityException.class, () -> {
filter.inbound("_action", request, channel, future);
future.actionGet();
fail("expected filter inbound to throw an authorization exception on authorization error");
} catch (ElasticsearchSecurityException e) {
assertThat(e.getMessage(), equalTo("authz failed"));
}
});
assertThat(e.getMessage(), equalTo("authz failed"));
}
public void testClientProfileRejectsNodeActions() throws Exception {
@ -182,11 +180,10 @@ public class ServerTransportFilterTests extends ESTestCase {
ServerTransportFilter filter = getNodeFilter();
TransportRequest request = mock(TransportRequest.class);
Authentication authentication = new Authentication(new User("test", "superuser"), new RealmRef("test", "test", "node1"), null);
final Collection<Role> userRoles = Collections.singletonList(SuperuserRole.INSTANCE);
doAnswer((i) -> {
ActionListener callback =
(ActionListener) i.getArguments()[1];
callback.onResponse(authentication.getUser().equals(i.getArguments()[0]) ? userRoles : Collections.emptyList());
callback.onResponse(authentication.getUser().equals(i.getArguments()[0]) ? ReservedRolesStore.SUPERUSER_ROLE : null);
return Void.TYPE;
}).when(authzService).roles(any(User.class), any(ActionListener.class));
doAnswer((i) -> {
@ -205,12 +202,12 @@ public class ServerTransportFilterTests extends ESTestCase {
filter.inbound(internalAction, request, channel, new PlainActionFuture<>());
verify(authcService).authenticate(eq(internalAction), eq(request), eq(null), any(ActionListener.class));
verify(authzService).roles(eq(authentication.getUser()), any(ActionListener.class));
verify(authzService).authorize(authentication, internalAction, request, userRoles, Collections.emptyList());
verify(authzService).authorize(authentication, internalAction, request, ReservedRolesStore.SUPERUSER_ROLE, null);
filter.inbound(nodeOrShardAction, request, channel, new PlainActionFuture<>());
verify(authcService).authenticate(eq(nodeOrShardAction), eq(request), eq(null), any(ActionListener.class));
verify(authzService, times(2)).roles(eq(authentication.getUser()), any(ActionListener.class));
verify(authzService).authorize(authentication, nodeOrShardAction, request, userRoles, Collections.emptyList());
verify(authzService).authorize(authentication, nodeOrShardAction, request, ReservedRolesStore.SUPERUSER_ROLE, null);
verifyNoMoreInteractions(authcService, authzService);
}

View File

@ -22,6 +22,7 @@ import org.elasticsearch.xpack.security.action.user.PutUserResponse;
import org.elasticsearch.xpack.security.authc.esnative.ESNativeRealmMigrateTool;
import org.elasticsearch.xpack.security.authc.support.SecuredString;
import org.elasticsearch.xpack.security.authz.RoleDescriptor;
import org.elasticsearch.xpack.security.authz.permission.FieldPermissions;
import org.elasticsearch.xpack.security.client.SecurityClient;
import org.elasticsearch.xpack.security.user.User;
import org.junit.Before;
@ -92,17 +93,19 @@ public class MigrateToolIT extends MigrateToolTestCase {
RoleDescriptor.IndicesPrivileges[] ips = rd.getIndicesPrivileges();
assertEquals(ips.length, 2);
for (RoleDescriptor.IndicesPrivileges ip : ips) {
final FieldPermissions fieldPermissions = new FieldPermissions(ip.getGrantedFields(), ip.getDeniedFields());
if (Arrays.equals(ip.getIndices(), new String[]{"index1", "index2"})) {
assertArrayEquals(ip.getPrivileges(), new String[]{"read", "write", "create_index", "indices:admin/refresh"});
assertTrue(ip.getFieldPermissions().hasFieldLevelSecurity());
assertTrue(ip.getFieldPermissions().grantsAccessTo("bar"));
assertTrue(ip.getFieldPermissions().grantsAccessTo("foo"));
assertTrue(fieldPermissions.hasFieldLevelSecurity());
assertTrue(fieldPermissions.grantsAccessTo("bar"));
assertTrue(fieldPermissions.grantsAccessTo("foo"));
assertNotNull(ip.getQuery());
assertThat(ip.getQuery().utf8ToString(), containsString("{\"bool\":{\"must_not\":{\"match\":{\"hidden\":true}}}}"));
assertThat(ip.getQuery().iterator().next().utf8ToString(),
containsString("{\"bool\":{\"must_not\":{\"match\":{\"hidden\":true}}}}"));
} else {
assertArrayEquals(ip.getIndices(), new String[]{"*"});
assertArrayEquals(ip.getPrivileges(), new String[]{"read"});
assertFalse(ip.getFieldPermissions().hasFieldLevelSecurity());
assertFalse(fieldPermissions.hasFieldLevelSecurity());
assertNull(ip.getQuery());
}
}