Merge branch 'master' into kibana-shield-users

Original commit: elastic/x-pack-elasticsearch@546084173a
This commit is contained in:
Lukas Olson 2016-01-25 16:38:29 -07:00
commit b67af868b3
37 changed files with 1537 additions and 1355 deletions

View File

@ -9,8 +9,10 @@ import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesActi
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
import org.elasticsearch.marvel.agent.settings.MarvelSettings;
import org.elasticsearch.shield.User;
import org.elasticsearch.shield.authz.Permission;
import org.elasticsearch.shield.authz.Privilege;
import org.elasticsearch.shield.authz.permission.Role;
import org.elasticsearch.shield.authz.privilege.ClusterPrivilege;
import org.elasticsearch.shield.authz.privilege.IndexPrivilege;
import org.elasticsearch.shield.authz.privilege.Privilege;
/**
*
@ -22,17 +24,17 @@ public class InternalMarvelUser extends User {
public static final InternalMarvelUser INSTANCE = new InternalMarvelUser(NAME, ROLE_NAMES);
public static final Permission.Global.Role ROLE = Permission.Global.Role.builder(ROLE_NAMES[0])
.cluster(Privilege.Cluster.get(new Privilege.Name(
public static final Role ROLE = Role.builder(ROLE_NAMES[0])
.cluster(ClusterPrivilege.get(new Privilege.Name(
PutIndexTemplateAction.NAME + "*",
GetIndexTemplatesAction.NAME + "*",
Privilege.Cluster.MONITOR.name().toString())))
ClusterPrivilege.MONITOR.name().toString())))
// we need all monitoring access
.add(Privilege.Index.MONITOR, "*")
.add(IndexPrivilege.MONITOR, "*")
// and full access to .marvel-es-* and .marvel-es-data indices
.add(Privilege.Index.ALL, MarvelSettings.MARVEL_INDICES_PREFIX + "*")
.add(IndexPrivilege.ALL, MarvelSettings.MARVEL_INDICES_PREFIX + "*")
// note, we don't need _license permission as we're taking the licenses
// directly form the license service.

View File

@ -23,7 +23,7 @@ import org.elasticsearch.shield.action.interceptor.RequestInterceptor;
import org.elasticsearch.shield.audit.AuditTrail;
import org.elasticsearch.shield.authc.AuthenticationService;
import org.elasticsearch.shield.authz.AuthorizationService;
import org.elasticsearch.shield.authz.Privilege;
import org.elasticsearch.shield.authz.privilege.HealthAndStatsPrivilege;
import org.elasticsearch.shield.crypto.CryptoService;
import org.elasticsearch.shield.license.ShieldLicenseState;
import org.elasticsearch.tasks.Task;
@ -41,7 +41,7 @@ import static org.elasticsearch.shield.support.Exceptions.authorizationError;
*/
public class ShieldActionFilter extends AbstractComponent implements ActionFilter {
private static final Predicate<String> LICENSE_EXPIRATION_ACTION_MATCHER = Privilege.HEALTH_AND_STATS.predicate();
private static final Predicate<String> LICENSE_EXPIRATION_ACTION_MATCHER = HealthAndStatsPrivilege.INSTANCE.predicate();
private final AuthenticationService authcService;
private final AuthorizationService authzService;

View File

@ -7,8 +7,10 @@ package org.elasticsearch.shield.admin;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
import org.elasticsearch.shield.User;
import org.elasticsearch.shield.authz.Permission;
import org.elasticsearch.shield.authz.Privilege;
import org.elasticsearch.shield.authz.permission.Role;
import org.elasticsearch.shield.authz.privilege.ClusterPrivilege;
import org.elasticsearch.shield.authz.privilege.IndexPrivilege;
import org.elasticsearch.shield.authz.privilege.Privilege;
/**
* User holder for the shield internal user that manages the {@code .shield}
@ -19,9 +21,9 @@ public class ShieldInternalUserHolder {
private static final String NAME = "__es_internal_user";
private static final String[] ROLES = new String[] { "__es_internal_role" };
public static final Permission.Global.Role ROLE = Permission.Global.Role.builder(ROLES[0])
.cluster(Privilege.Cluster.get(new Privilege.Name(PutIndexTemplateAction.NAME, "cluster:admin/shield/realm/cache/clear*", "cluster:admin/shield/roles/cache/clear*")))
.add(Privilege.Index.ALL, ShieldTemplateService.SHIELD_ADMIN_INDEX_NAME)
public static final Role ROLE = Role.builder(ROLES[0])
.cluster(ClusterPrivilege.get(new Privilege.Name(PutIndexTemplateAction.NAME, "cluster:admin/shield/realm/cache/clear*", "cluster:admin/shield/roles/cache/clear*")))
.add(IndexPrivilege.ALL, ShieldTemplateService.SHIELD_ADMIN_INDEX_NAME)
.build();
private static final User SHIELD_INTERNAL_USER = new User(NAME, ROLES);

View File

@ -43,18 +43,18 @@ import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.gateway.GatewayService;
import org.elasticsearch.rest.RestRequest;
import org.elasticsearch.shield.admin.ShieldInternalUserHolder;
import org.elasticsearch.shield.authz.privilege.SystemPrivilege;
import org.elasticsearch.xpack.XPackPlugin;
import org.elasticsearch.shield.User;
import org.elasticsearch.shield.audit.AuditTrail;
import org.elasticsearch.shield.authc.AuthenticationService;
import org.elasticsearch.shield.authc.AuthenticationToken;
import org.elasticsearch.shield.authz.Privilege;
import org.elasticsearch.shield.authz.privilege.Privilege;
import org.elasticsearch.shield.rest.RemoteHostHeader;
import org.elasticsearch.shield.transport.filter.ShieldIpFilterRule;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.Transport;
import org.elasticsearch.transport.TransportMessage;
import org.elasticsearch.xpack.XPackPlugin;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
@ -417,7 +417,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl
public void accessGranted(User user, String action, TransportMessage<?> message) {
if (!principalIsAuditor(user.principal())) {
// special treatment for internal system actions - only log if explicitly told to
if ((user.isSystem() && Privilege.SYSTEM.predicate().test(action)) || ShieldInternalUserHolder.isShieldInternalUser(user)) {
if ((user.isSystem() && SystemPrivilege.INSTANCE.predicate().test(action)) || ShieldInternalUserHolder.isShieldInternalUser(user)) {
if (events.contains(SYSTEM_ACCESS_GRANTED)) {
try {
enqueue(message("access_granted", action, user, indices(message), message), "access_granted");

View File

@ -10,8 +10,9 @@ import org.elasticsearch.action.admin.indices.mapping.put.PutMappingAction;
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
import org.elasticsearch.action.bulk.BulkAction;
import org.elasticsearch.shield.User;
import org.elasticsearch.shield.authz.Permission;
import org.elasticsearch.shield.authz.Privilege;
import org.elasticsearch.shield.authz.permission.Role;
import org.elasticsearch.shield.authz.privilege.ClusterPrivilege;
import org.elasticsearch.shield.authz.privilege.IndexPrivilege;
/**
*
@ -20,13 +21,13 @@ public class IndexAuditUserHolder {
private static final String NAME = "__indexing_audit_user";
private static final String[] ROLE_NAMES = new String[] { "__indexing_audit_role" };
public static final Permission.Global.Role ROLE = Permission.Global.Role.builder(ROLE_NAMES[0])
.cluster(Privilege.Cluster.action(PutIndexTemplateAction.NAME))
.add(Privilege.Index.CREATE_INDEX, IndexAuditTrail.INDEX_NAME_PREFIX + "*")
.add(Privilege.Index.INDEX, IndexAuditTrail.INDEX_NAME_PREFIX + "*")
.add(Privilege.Index.action(IndicesExistsAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*")
.add(Privilege.Index.action(BulkAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*")
.add(Privilege.Index.action(PutMappingAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*")
public static final Role ROLE = Role.builder(ROLE_NAMES[0])
.cluster(ClusterPrivilege.action(PutIndexTemplateAction.NAME))
.add(IndexPrivilege.CREATE_INDEX, IndexAuditTrail.INDEX_NAME_PREFIX + "*")
.add(IndexPrivilege.INDEX, IndexAuditTrail.INDEX_NAME_PREFIX + "*")
.add(IndexPrivilege.action(IndicesExistsAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*")
.add(IndexPrivilege.action(BulkAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*")
.add(IndexPrivilege.action(PutMappingAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*")
.build();
private final User user;

View File

@ -20,7 +20,8 @@ import org.elasticsearch.shield.User;
import org.elasticsearch.shield.admin.ShieldInternalUserHolder;
import org.elasticsearch.shield.audit.AuditTrail;
import org.elasticsearch.shield.authc.AuthenticationToken;
import org.elasticsearch.shield.authz.Privilege;
import org.elasticsearch.shield.authz.privilege.Privilege;
import org.elasticsearch.shield.authz.privilege.SystemPrivilege;
import org.elasticsearch.shield.rest.RemoteHostHeader;
import org.elasticsearch.shield.transport.filter.ShieldIpFilterRule;
import org.elasticsearch.transport.Transport;
@ -195,7 +196,7 @@ public class LoggingAuditTrail extends AbstractLifecycleComponent<LoggingAuditTr
String indices = indicesString(message);
// special treatment for internal system actions - only log on trace
if ((user.isSystem() && Privilege.SYSTEM.predicate().test(action)) || ShieldInternalUserHolder.isShieldInternalUser(user)) {
if ((user.isSystem() && SystemPrivilege.INSTANCE.predicate().test(action)) || ShieldInternalUserHolder.isShieldInternalUser(user)) {
if (logger.isTraceEnabled()) {
if (indices != null) {
logger.trace("{}[transport] [access_granted]\t{}, {}, action=[{}], indices=[{}], request=[{}]", prefix, originAttributes(message, transport), principal(user), action, indices, message.getClass().getSimpleName());

View File

@ -9,6 +9,7 @@ import org.elasticsearch.common.inject.multibindings.Multibinder;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.authc.esnative.ESNativeUsersStore;
import org.elasticsearch.shield.authz.esnative.ESNativeRolesStore;
import org.elasticsearch.shield.authz.permission.Role;
import org.elasticsearch.shield.authz.store.CompositeRolesStore;
import org.elasticsearch.shield.authz.store.FileRolesStore;
import org.elasticsearch.shield.authz.store.RolesStore;
@ -22,21 +23,21 @@ import java.util.Set;
*/
public class AuthorizationModule extends AbstractShieldModule.Node {
private final Set<Permission.Global.Role> reservedRoles = new HashSet<>();
private final Set<Role> reservedRoles = new HashSet<>();
public AuthorizationModule(Settings settings) {
super(settings);
}
public void registerReservedRole(Permission.Global.Role role) {
public void registerReservedRole(Role role) {
reservedRoles.add(role);
}
@Override
protected void configureNode() {
Multibinder<Permission.Global.Role> reservedRolesBinder = Multibinder.newSetBinder(binder(), Permission.Global.Role.class);
for (Permission.Global.Role reservedRole : reservedRoles) {
Multibinder<Role> reservedRolesBinder = Multibinder.newSetBinder(binder(), Role.class);
for (Role reservedRole : reservedRoles) {
reservedRolesBinder.addBinding().toInstance(reservedRole);
}

View File

@ -28,6 +28,12 @@ import org.elasticsearch.shield.authc.AuthenticationFailureHandler;
import org.elasticsearch.shield.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.shield.authz.indicesresolver.DefaultIndicesAndAliasesResolver;
import org.elasticsearch.shield.authz.indicesresolver.IndicesAndAliasesResolver;
import org.elasticsearch.shield.authz.permission.ClusterPermission;
import org.elasticsearch.shield.authz.permission.GlobalPermission;
import org.elasticsearch.shield.authz.permission.Role;
import org.elasticsearch.shield.authz.permission.RunAsPermission;
import org.elasticsearch.shield.authz.privilege.ClusterPrivilege;
import org.elasticsearch.shield.authz.privilege.IndexPrivilege;
import org.elasticsearch.shield.authz.store.RolesStore;
import org.elasticsearch.transport.TransportRequest;
@ -70,15 +76,15 @@ public class InternalAuthorizationService extends AbstractComponent implements A
@Override
public List<String> authorizedIndicesAndAliases(User user, String action) {
String[] roles = user.roles();
if (roles.length == 0) {
String[] rolesNames = user.roles();
if (rolesNames.length == 0) {
return Collections.emptyList();
}
List<Predicate<String>> predicates = new ArrayList<>();
for (String role : roles) {
Permission.Global.Role global = rolesStore.role(role);
if (global != null) {
predicates.add(global.indices().allowedIndicesMatcher(action));
for (String roleName : rolesNames) {
Role role = rolesStore.role(roleName);
if (role != null) {
predicates.add(role.indices().allowedIndicesMatcher(action));
}
}
@ -107,7 +113,7 @@ public class InternalAuthorizationService extends AbstractComponent implements A
throw denial(user, action, request);
}
Permission.Global permission = permission(user.roles());
GlobalPermission permission = permission(user.roles());
final boolean isRunAs = user.runAs() != null;
// permission can be null as it might be that the user's role
// is unknown
@ -123,7 +129,7 @@ public class InternalAuthorizationService extends AbstractComponent implements A
// check if the request is a run as request
if (isRunAs) {
// first we must authorize for the RUN_AS action
Permission.RunAs runAs = permission.runAs();
RunAsPermission runAs = permission.runAs();
if (runAs != null && runAs.check(user.runAs().principal())) {
grantRunAs(user, action, request);
permission = permission(user.runAs().roles());
@ -140,8 +146,8 @@ public class InternalAuthorizationService extends AbstractComponent implements A
// first, we'll check if the action is a cluster action. If it is, we'll only check it
// against the cluster permissions
if (Privilege.Cluster.ACTION_MATCHER.test(action)) {
Permission.Cluster cluster = permission.cluster();
if (ClusterPrivilege.ACTION_MATCHER.test(action)) {
ClusterPermission cluster = permission.cluster();
if (cluster != null && cluster.check(action)) {
request.putInContext(INDICES_PERMISSIONS_KEY, IndicesAccessControl.ALLOW_ALL);
grant(user, action, request);
@ -151,7 +157,7 @@ public class InternalAuthorizationService extends AbstractComponent implements A
}
// ok... this is not a cluster action, let's verify it's an indices action
if (!Privilege.Index.ACTION_MATCHER.test(action)) {
if (!IndexPrivilege.ACTION_MATCHER.test(action)) {
throw denial(user, action, request);
}
@ -189,7 +195,7 @@ public class InternalAuthorizationService extends AbstractComponent implements A
}
//if we are creating an index we need to authorize potential aliases created at the same time
if (Privilege.Index.CREATE_INDEX_MATCHER.test(action)) {
if (IndexPrivilege.CREATE_INDEX_MATCHER.test(action)) {
assert request instanceof CreateIndexRequest;
Set<Alias> aliases = ((CreateIndexRequest) request).aliases();
if (!aliases.isEmpty()) {
@ -209,21 +215,21 @@ public class InternalAuthorizationService extends AbstractComponent implements A
grant(user, action, request);
}
private Permission.Global permission(String[] roleNames) {
private GlobalPermission permission(String[] roleNames) {
if (roleNames.length == 0) {
return Permission.Global.NONE;
return GlobalPermission.NONE;
}
if (roleNames.length == 1) {
Permission.Global.Role role = rolesStore.role(roleNames[0]);
return role == null ? Permission.Global.NONE : role;
Role role = rolesStore.role(roleNames[0]);
return role == null ? GlobalPermission.NONE : role;
}
// we'll take all the roles and combine their associated permissions
Permission.Global.Compound.Builder roles = Permission.Global.Compound.builder();
GlobalPermission.Compound.Builder roles = GlobalPermission.Compound.builder();
for (String roleName : roleNames) {
Permission.Global role = rolesStore.role(roleName);
GlobalPermission role = rolesStore.role(roleName);
if (role != null) {
roles.add(role);
}

View File

@ -1,656 +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.shield.authz;
import org.elasticsearch.cluster.metadata.AliasOrIndex;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.shield.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.shield.support.AutomatonPredicate;
import org.elasticsearch.shield.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;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
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;
/**
* Represents a permission in the system. There are 3 types of permissions:
*
* <ul>
* <li>
* Cluster - a permission that is based on privileges for cluster wide actions
* </li>
* <li>
* Indices - a permission that is based on privileges for index related actions executed
* on specific indices
* </li>
* <li>RunAs - a permissions that is based on a general privilege that contains patterns of users that this
* user can execute a request as
* </li>
* <li>
* Global - a composite permission that combines a both cluster &amp; indices permissions
* </li>
* </ul>
*/
public interface Permission {
boolean isEmpty();
class Global implements Permission {
public static final Global NONE = new Global(Cluster.Core.NONE, Indices.Core.NONE, RunAs.Core.NONE);
private final Cluster cluster;
private final Indices indices;
private final RunAs runAs;
Global(Cluster cluster, Indices indices, RunAs runAs) {
this.cluster = cluster;
this.indices = indices;
this.runAs = runAs;
}
public Cluster cluster() {
return cluster;
}
public Indices indices() {
return indices;
}
public RunAs runAs() {
return runAs;
}
@Override
public boolean isEmpty() {
return (cluster == null || cluster.isEmpty()) && (indices == null || indices.isEmpty()) && (runAs == null || runAs.isEmpty());
}
/**
* Returns whether at least group encapsulated by this indices permissions is auhorized the 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 Role extends Global {
private final String name;
private Role(String name, Cluster.Core cluster, Indices.Core indices, RunAs.Core runAs) {
super(cluster, indices, runAs);
this.name = name;
}
public String name() {
return name;
}
@Override
public Cluster.Core cluster() {
return (Cluster.Core) super.cluster();
}
@Override
public Indices.Core indices() {
return (Indices.Core) super.indices();
}
@Override
public RunAs.Core runAs() {
return (RunAs.Core) super.runAs();
}
public static Builder builder(String name) {
return new Builder(name);
}
public static Builder builder(RoleDescriptor rd) {
return new Builder(rd);
}
public static class Builder {
private final String name;
private Cluster.Core cluster = Cluster.Core.NONE;
private RunAs.Core runAs = RunAs.Core.NONE;
private List<Indices.Group> groups = new ArrayList<>();
private Builder(String name) {
this.name = name;
}
private Builder(RoleDescriptor rd) {
this.name = rd.getName();
this.cluster(Privilege.Cluster.get((new Privilege.Name(rd.getClusterPattern()))));
for (RoleDescriptor.IndicesPrivileges iGroup : rd.getIndicesPrivileges()) {
this.add(iGroup.getFields() == null ? null : Arrays.asList(iGroup.getFields()),
iGroup.getQuery(),
Privilege.Index.get(new Privilege.Name(iGroup.getPrivileges())),
iGroup.getIndices());
}
String[] rdRunAs = rd.getRunAs();
if (rdRunAs != null && rdRunAs.length > 0) {
this.runAs(new Privilege.General(new Privilege.Name(rdRunAs), rdRunAs));
}
}
// FIXME we should throw an exception if we have already set cluster or runAs...
public Builder cluster(Privilege.Cluster privilege) {
cluster = new Cluster.Core(privilege);
return this;
}
public Builder runAs(Privilege.General privilege) {
runAs = new RunAs.Core(privilege);
return this;
}
public Builder add(Privilege.Index privilege, String... indices) {
groups.add(new Indices.Group(privilege, null, null, indices));
return this;
}
public Builder add(List<String> fields, BytesReference query, Privilege.Index privilege, String... indices) {
groups.add(new Indices.Group(privilege, fields, query, indices));
return this;
}
public Role build() {
Indices.Core indices = groups.isEmpty() ? Indices.Core.NONE : new Indices.Core(groups.toArray(new Indices.Group[groups.size()]));
return new Role(name, cluster, indices, runAs);
}
}
}
static class Compound extends Global {
public Compound(List<Global> globals) {
super(new Cluster.Globals(globals), new Indices.Globals(globals), new RunAs.Globals(globals));
}
public static Builder builder() {
return new Builder();
}
public static class Builder {
private List<Global> globals = new ArrayList<>();
private Builder() {
}
public Builder add(Global global) {
globals.add(global);
return this;
}
public Compound build() {
return new Compound(Collections.unmodifiableList(globals));
}
}
}
}
static interface Cluster extends Permission {
boolean check(String action);
public static class Core implements Cluster {
public static final Core NONE = new Core(Privilege.Cluster.NONE) {
@Override
public boolean check(String action) {
return false;
}
@Override
public boolean isEmpty() {
return true;
}
};
private final Privilege.Cluster privilege;
private final Predicate<String> predicate;
private Core(Privilege.Cluster privilege) {
this.privilege = privilege;
this.predicate = privilege.predicate();
}
public Privilege.Cluster privilege() {
return privilege;
}
@Override
public boolean check(String action) {
return predicate.test(action);
}
@Override
public boolean isEmpty() {
return false;
}
}
static class Globals implements Cluster {
private final List<Global> globals;
public Globals(List<Global> globals) {
this.globals = globals;
}
@Override
public boolean check(String action) {
if (globals == null) {
return false;
}
for (Global global : globals) {
if (global.cluster().check(action)) {
return true;
}
}
return false;
}
@Override
public boolean isEmpty() {
if (globals == null || globals.isEmpty()) {
return true;
}
for (Global global : globals) {
if (!global.isEmpty()) {
return false;
}
}
return true;
}
}
}
static interface Indices extends Permission, Iterable<Indices.Group> {
Map<String, IndicesAccessControl.IndexAccessControl> authorize(String action, Set<String> requestedIndicesOrAliases, MetaData metaData);
public static class Core implements Indices {
public static final Core NONE = new Core() {
@Override
public Iterator<Group> iterator() {
return Collections.emptyIterator();
}
@Override
public boolean isEmpty() {
return true;
}
};
private final Function<String, Predicate<String>> loadingFunction;
private final ConcurrentHashMap<String, Predicate<String>> allowedIndicesMatchersForAction = new ConcurrentHashMap<>();
private final Group[] groups;
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));
}
}
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 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<String>> rolesFieldsByIndex = 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());
}
}
for (Permission.Indices.Group group : groups) {
if (group.check(action, indexOrAlias)) {
granted = true;
for (String index : concreteIndices) {
if (group.getFields() != null) {
Set<String> roleFields = rolesFieldsByIndex.get(index);
if (roleFields == null) {
roleFields = new HashSet<>();
rolesFieldsByIndex.put(index, roleFields);
}
roleFields.addAll(group.getFields());
}
if (group.getQuery() != null) {
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);
}
Set<String> roleFields = rolesFieldsByIndex.get(index);
if (roleFields != null) {
roleFields = unmodifiableSet(roleFields);
}
indexPermissions.put(index, new IndicesAccessControl.IndexAccessControl(entry.getValue(), roleFields, roleQueries));
}
return unmodifiableMap(indexPermissions);
}
}
public static class Globals implements Indices {
private final List<Global> globals;
public Globals(List<Global> globals) {
this.globals = globals;
}
@Override
public Iterator<Group> iterator() {
return globals == null || globals.isEmpty() ?
Collections.<Group>emptyIterator() :
new Iter(globals);
}
@Override
public boolean isEmpty() {
if (globals == null || globals.isEmpty()) {
return true;
}
for (Global global : globals) {
if (!global.indices().isEmpty()) {
return false;
}
}
return true;
}
@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 (Global 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 (indicesAccessControl == null) {
return emptyMap();
} else {
return unmodifiableMap(indicesAccessControl);
}
}
static class Iter implements Iterator<Group> {
private final Iterator<Global> globals;
private Iterator<Group> current;
Iter(List<Global> globals) {
this.globals = globals.iterator();
advance();
}
@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()) {
Indices indices = globals.next().indices();
if (!indices.isEmpty()) {
current = indices.iterator();
return;
}
}
current = null;
}
}
}
public static class Group {
private final Privilege.Index privilege;
private final Predicate<String> actionMatcher;
private final String[] indices;
private final Predicate<String> indexNameMatcher;
private final List<String> fields;
private final BytesReference query;
public Group(Privilege.Index privilege, @Nullable List<String> fields, @Nullable 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.fields = fields;
this.query = query;
}
public Privilege.Index privilege() {
return privilege;
}
public String[] indices() {
return indices;
}
@Nullable
public List<String> getFields() {
return fields;
}
@Nullable
public BytesReference getQuery() {
return query;
}
public boolean indexNameMatch(String index) {
return indexNameMatcher.test(index);
}
public boolean check(String action, String index) {
assert index != null;
return actionMatcher.test(action) && indexNameMatcher.test(index);
}
}
}
// FIXME let's split this up, 11 classes before this in a single file that aren't documented and are extremely important
static interface RunAs extends Permission {
/**
* Checks if this permission grants run as to the specified user
*/
boolean check(String username);
class Core implements RunAs {
public static final Core NONE = new Core(Privilege.General.NONE);
private final Privilege.General privilege;
private final Predicate<String> predicate;
public Core(Privilege.General 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 RunAs {
private final List<Global> globals;
public Globals(List<Global> globals) {
this.globals = globals;
}
@Override
public boolean check(String username) {
if (globals == null) {
return false;
}
for (Global global : globals) {
if (global.runAs().check(username)) {
return true;
}
}
return false;
}
@Override
public boolean isEmpty() {
if (globals == null || globals.isEmpty()) {
return true;
}
for (Global global : globals) {
if (!global.isEmpty()) {
return false;
}
}
return true;
}
}
}
}

View File

@ -1,468 +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.shield.authz;
import dk.brics.automaton.Automaton;
import dk.brics.automaton.BasicAutomata;
import dk.brics.automaton.BasicOperations;
import org.elasticsearch.action.admin.indices.create.CreateIndexAction;
import org.elasticsearch.action.get.GetAction;
import org.elasticsearch.action.get.MultiGetAction;
import org.elasticsearch.action.search.MultiSearchAction;
import org.elasticsearch.action.search.SearchAction;
import org.elasticsearch.action.suggest.SuggestAction;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.shield.support.AutomatonPredicate;
import org.elasticsearch.shield.support.Automatons;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
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.shield.support.Automatons.patterns;
/**
*
*/
public abstract class Privilege<P extends Privilege<P>> {
static final String SUB_ACTION_SUFFIX_PATTERN = "*";
public static final System SYSTEM = new System();
public static final General HEALTH_AND_STATS = new General("health_and_stats",
"cluster:monitor/health*",
"cluster:monitor/stats*",
"indices:monitor/stats*",
"cluster:monitor/nodes/stats*");
protected final Name name;
private Privilege(Name name) {
this.name = name;
}
public Name name() {
return name;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Privilege privilege = (Privilege) o;
if (name != null ? !name.equals(privilege.name) : privilege.name != null) return false;
return true;
}
@Override
public int hashCode() {
return name != null ? name.hashCode() : 0;
}
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 static class System extends Privilege<System> {
private static final Predicate<String> INTERNAL_PREDICATE = new AutomatonPredicate(patterns("internal:*"));
protected static final Predicate<String> PREDICATE = new AutomatonPredicate(patterns(
"internal:*",
"indices:monitor/*", // added for marvel
"cluster:monitor/*", // added for marvel
"cluster:admin/reroute", // added for DiskThresholdDecider.DiskListener
"indices:admin/mapping/put" // ES 2.0 MappingUpdatedAction - updateMappingOnMasterSynchronously
));
private System() {
super(new Name("internal"));
}
@Override
public Predicate<String> predicate() {
return PREDICATE;
}
@Override
public boolean implies(System other) {
return true;
}
}
public static class General extends AutomatonPrivilege<General> {
public static final General NONE = new General(Name.NONE, BasicAutomata.makeEmpty());
public General(String name, String... patterns) {
super(name, patterns);
}
public General(Name name, String... patterns) {
super(name, patterns);
}
public General(Name name, Automaton automaton) {
super(name, automaton);
}
@Override
protected General create(Name name, Automaton automaton) {
return new General(name, automaton);
}
@Override
protected General none() {
return NONE;
}
}
public static class Index extends AutomatonPrivilege<Index> {
public static final Index NONE = new Index(Name.NONE, BasicAutomata.makeEmpty());
public static final Index ALL = new Index(Name.ALL, "indices:*");
public static final Index MANAGE = new Index("manage", "indices:monitor/*", "indices:admin/*");
public static final Index CREATE_INDEX = new Index("create_index", CreateIndexAction.NAME);
public static final Index MANAGE_ALIASES = new Index("manage_aliases", "indices:admin/aliases*");
public static final Index MONITOR = new Index("monitor", "indices:monitor/*");
public static final Index DATA_ACCESS = new Index("data_access", "indices:data/*");
public static final Index CRUD = new Index("crud", "indices:data/write/*", "indices:data/read/*");
public static final Index READ = new Index("read", "indices:data/read/*");
public static final Index SEARCH = new Index("search", SearchAction.NAME + "*", MultiSearchAction.NAME + "*", SuggestAction.NAME + "*");
public static final Index GET = new Index("get", GetAction.NAME + "*", MultiGetAction.NAME + "*");
public static final Index SUGGEST = new Index("suggest", SuggestAction.NAME + "*");
public static final Index INDEX = new Index("index", "indices:data/write/index*", "indices:data/write/update*");
public static final Index DELETE = new Index("delete", "indices:data/write/delete*");
public static final Index WRITE = new Index("write", "indices:data/write/*");
private static final Set<Index> values = new CopyOnWriteArraySet<>();
static {
values.add(NONE);
values.add(ALL);
values.add(MANAGE);
values.add(CREATE_INDEX);
values.add(MANAGE_ALIASES);
values.add(MONITOR);
values.add(DATA_ACCESS);
values.add(CRUD);
values.add(READ);
values.add(SEARCH);
values.add(GET);
values.add(SUGGEST);
values.add(INDEX);
values.add(DELETE);
values.add(WRITE);
}
public static final Predicate<String> ACTION_MATCHER = ALL.predicate();
public static final Predicate<String> CREATE_INDEX_MATCHER = CREATE_INDEX.predicate();
static Set<Index> values() {
return values;
}
private static final ConcurrentHashMap<Name, Index> cache = new ConcurrentHashMap<>();
private Index(String name, String... patterns) {
super(name, patterns);
}
private Index(Name name, String... patterns) {
super(name, patterns);
}
private Index(Name name, Automaton automaton) {
super(name, automaton);
}
public static void addCustom(String name, String... actionPatterns) {
for (String pattern : actionPatterns) {
if (!Index.ACTION_MATCHER.test(pattern)) {
throw new IllegalArgumentException("cannot register custom index privilege [" + name + "]. index action must follow the 'indices:*' format");
}
}
Index custom = new Index(name, actionPatterns);
if (values.contains(custom)) {
throw new IllegalArgumentException("cannot register custom index privilege [" + name + "] as it already exists.");
}
values.add(custom);
}
@Override
protected Index create(Name name, Automaton automaton) {
if (name == Name.NONE) {
return NONE;
}
return new Index(name, automaton);
}
@Override
protected Index none() {
return NONE;
}
public static Index action(String action) {
return new Index(action, actionToPattern(action));
}
public static Index get(Name name) {
return cache.computeIfAbsent(name, (theName) -> {
Index index = NONE;
for (String part : theName.parts) {
index = index == NONE ? resolve(part) : index.plus(resolve(part));
}
return index;
});
}
public static Index union(Index... indices) {
Index result = NONE;
for (Index index : indices) {
result = result.plus(index);
}
return result;
}
private static Index resolve(String name) {
name = name.toLowerCase(Locale.ROOT);
if (ACTION_MATCHER.test(name)) {
return action(name);
}
for (Index index : values) {
if (name.toLowerCase(Locale.ROOT).equals(index.name.toString())) {
return index;
}
}
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");
}
}
public static class Cluster extends AutomatonPrivilege<Cluster> {
public static final Cluster NONE = new Cluster(Name.NONE, BasicAutomata.makeEmpty());
public static final Cluster ALL = new Cluster(Name.ALL, "cluster:*", "indices:admin/template/*");
public static final Cluster MONITOR = new Cluster("monitor", "cluster:monitor/*");
public static final Cluster MANAGE_SHIELD = new Cluster("manage_shield", "cluster:admin/shield/*");
final static Predicate<String> ACTION_MATCHER = Privilege.Cluster.ALL.predicate();
private static final Set<Cluster> values = new CopyOnWriteArraySet<>();
static {
values.add(NONE);
values.add(ALL);
values.add(MONITOR);
values.add(MANAGE_SHIELD);
}
static Set<Cluster> values() {
return values;
}
private static final ConcurrentHashMap<Name, Cluster> cache = new ConcurrentHashMap<>();
private Cluster(String name, String... patterns) {
super(name, patterns);
}
private Cluster(Name name, String... patterns) {
super(name, patterns);
}
private Cluster(Name name, Automaton automaton) {
super(name, automaton);
}
public static void addCustom(String name, String... actionPatterns) {
for (String pattern : actionPatterns) {
if (!Cluster.ACTION_MATCHER.test(pattern)) {
throw new IllegalArgumentException("cannot register custom cluster privilege [" + name + "]. cluster aciton must follow the 'cluster:*' format");
}
}
Cluster custom = new Cluster(name, actionPatterns);
if (values.contains(custom)) {
throw new IllegalArgumentException("cannot register custom cluster privilege [" + name + "] as it already exists.");
}
values.add(custom);
}
@Override
protected Cluster create(Name name, Automaton automaton) {
return new Cluster(name, automaton);
}
@Override
protected Cluster none() {
return NONE;
}
public static Cluster action(String action) {
String pattern = actionToPattern(action);
return new Cluster(action, pattern);
}
public static Cluster get(Name name) {
return cache.computeIfAbsent(name, (theName) -> {
Cluster cluster = NONE;
for (String part : theName.parts) {
cluster = cluster == NONE ? resolve(part) : cluster.plus(resolve(part));
}
return cluster;
});
}
private static Cluster resolve(String name) {
name = name.toLowerCase(Locale.ROOT);
if (ACTION_MATCHER.test(name)) {
return action(name);
}
for (Cluster 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");
}
}
static String actionToPattern(String text) {
return text + SUB_ACTION_SUFFIX_PATTERN;
}
@SuppressWarnings("unchecked")
private static abstract class AutomatonPrivilege<P extends AutomatonPrivilege<P>> extends Privilege<P> {
protected final Automaton automaton;
private AutomatonPrivilege(String name, String... patterns) {
super(new Name(name));
this.automaton = patterns(patterns);
}
private AutomatonPrivilege(Name name, String... patterns) {
super(name);
this.automaton = patterns(patterns);
}
private AutomatonPrivilege(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));
}
protected P minus(P other) {
if (other.implies((P) this)) {
return none();
}
if (other == none() || !this.implies(other)) {
return (P) this;
}
return create(name.remove(other.name), Automatons.minusAndDeterminize(automaton, other.automaton));
}
@Override
public boolean implies(P other) {
return BasicOperations.subsetOf(other.automaton, automaton);
}
@Override
public String toString() {
return name.toString();
}
protected abstract P create(Name name, Automaton automaton);
protected abstract P none();
}
public static class Name {
public static final Name NONE = new Name("none");
public static final Name ALL = new Name("all");
private 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();
}
}
}

View File

@ -5,6 +5,8 @@
*/
package org.elasticsearch.shield.authz;
import org.elasticsearch.shield.authz.privilege.SystemPrivilege;
import java.util.function.Predicate;
/**
@ -16,7 +18,7 @@ public class SystemRole {
public static final String NAME = "__es_system_role";
private static final Predicate<String> PREDICATE = Privilege.SYSTEM.predicate();
private static final Predicate<String> PREDICATE = SystemPrivilege.INSTANCE.predicate();
private SystemRole() {
}

View File

@ -29,7 +29,6 @@ import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.component.AbstractComponent;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.inject.Provider;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
@ -48,7 +47,7 @@ import org.elasticsearch.shield.action.authz.cache.ClearRolesCacheResponse;
import org.elasticsearch.shield.admin.ShieldInternalUserHolder;
import org.elasticsearch.shield.admin.ShieldTemplateService;
import org.elasticsearch.shield.authc.AuthenticationService;
import org.elasticsearch.shield.authz.Permission.Global.Role;
import org.elasticsearch.shield.authz.permission.Role;
import org.elasticsearch.shield.authz.RoleDescriptor;
import org.elasticsearch.shield.authz.store.RolesStore;
import org.elasticsearch.shield.client.ShieldClient;

View File

@ -0,0 +1,92 @@
/*
* 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.shield.authz.permission;
import org.elasticsearch.shield.authz.privilege.ClusterPrivilege;
import java.util.List;
import java.util.function.Predicate;
/**
* A permission that is based on privileges for cluster wide actions
*/
public interface ClusterPermission extends Permission {
boolean check(String action);
public static class Core implements ClusterPermission {
public static final Core NONE = new Core(ClusterPrivilege.NONE) {
@Override
public boolean check(String action) {
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) {
return predicate.test(action);
}
@Override
public boolean isEmpty() {
return false;
}
}
static class Globals implements ClusterPermission {
private final List<GlobalPermission> globals;
public Globals(List<GlobalPermission> globals) {
this.globals = globals;
}
@Override
public boolean check(String action) {
if (globals == null) {
return false;
}
for (GlobalPermission global : globals) {
if (global.cluster().check(action)) {
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;
}
}
}

View File

@ -0,0 +1,99 @@
/*
* 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.shield.authz.permission;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.shield.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 {
public 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

@ -0,0 +1,323 @@
/*
* 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.shield.authz.permission;
import org.elasticsearch.cluster.metadata.AliasOrIndex;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.shield.authz.accesscontrol.IndicesAccessControl;
import org.elasticsearch.shield.authz.privilege.IndexPrivilege;
import org.elasticsearch.shield.support.AutomatonPredicate;
import org.elasticsearch.shield.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;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
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;
/**
* A permission that is based on privileges for index related actions executed
* on specific indices
*/
public interface IndicesPermission extends Permission, Iterable<IndicesPermission.Group> {
Map<String, IndicesAccessControl.IndexAccessControl> authorize(String action, Set<String> requestedIndicesOrAliases, MetaData metaData);
public static class Core implements IndicesPermission {
public static final Core NONE = new Core() {
@Override
public Iterator<Group> iterator() {
return Collections.emptyIterator();
}
@Override
public boolean isEmpty() {
return true;
}
};
private final Function<String, Predicate<String>> loadingFunction;
private final ConcurrentHashMap<String, Predicate<String>> allowedIndicesMatchersForAction = new ConcurrentHashMap<>();
private final Group[] groups;
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));
}
}
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 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<String>> rolesFieldsByIndex = 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());
}
}
for (Group group : groups) {
if (group.check(action, indexOrAlias)) {
granted = true;
for (String index : concreteIndices) {
if (group.getFields() != null) {
Set<String> roleFields = rolesFieldsByIndex.get(index);
if (roleFields == null) {
roleFields = new HashSet<>();
rolesFieldsByIndex.put(index, roleFields);
}
roleFields.addAll(group.getFields());
}
if (group.getQuery() != null) {
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);
}
Set<String> roleFields = rolesFieldsByIndex.get(index);
if (roleFields != null) {
roleFields = unmodifiableSet(roleFields);
}
indexPermissions.put(index, new IndicesAccessControl.IndexAccessControl(entry.getValue(), roleFields, roleQueries));
}
return unmodifiableMap(indexPermissions);
}
}
public static 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 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 (indicesAccessControl == null) {
return emptyMap();
} else {
return unmodifiableMap(indicesAccessControl);
}
}
static class Iter implements Iterator<Group> {
private final Iterator<GlobalPermission> globals;
private Iterator<Group> current;
Iter(List<GlobalPermission> globals) {
this.globals = globals.iterator();
advance();
}
@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;
}
}
}
public static class Group {
private final IndexPrivilege privilege;
private final Predicate<String> actionMatcher;
private final String[] indices;
private final Predicate<String> indexNameMatcher;
private final List<String> fields;
private final BytesReference query;
public Group(IndexPrivilege privilege, @Nullable List<String> fields, @Nullable 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.fields = fields;
this.query = query;
}
public IndexPrivilege privilege() {
return privilege;
}
public String[] indices() {
return indices;
}
@Nullable
public List<String> getFields() {
return fields;
}
@Nullable
public BytesReference getQuery() {
return query;
}
public boolean indexNameMatch(String index) {
return indexNameMatcher.test(index);
}
public boolean check(String action, String index) {
assert index != null;
return actionMatcher.test(action) && indexNameMatcher.test(index);
}
}
}

View File

@ -0,0 +1,15 @@
/*
* 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.shield.authz.permission;
/**
* Represents a permission in the system.
*/
public interface Permission {
boolean isEmpty();
}

View File

@ -0,0 +1,110 @@
/*
* 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.shield.authz.permission;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.shield.authz.RoleDescriptor;
import org.elasticsearch.shield.authz.privilege.ClusterPrivilege;
import org.elasticsearch.shield.authz.privilege.GeneralPrivilege;
import org.elasticsearch.shield.authz.privilege.IndexPrivilege;
import org.elasticsearch.shield.authz.privilege.Privilege;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
*
*/
public class Role extends GlobalPermission {
private final String name;
private Role(String name, ClusterPermission.Core cluster, IndicesPermission.Core indices, RunAsPermission.Core runAs) {
super(cluster, indices, runAs);
this.name = name;
}
public String name() {
return name;
}
@Override
public ClusterPermission.Core cluster() {
return (ClusterPermission.Core) super.cluster();
}
@Override
public IndicesPermission.Core indices() {
return (IndicesPermission.Core) super.indices();
}
@Override
public RunAsPermission.Core runAs() {
return (RunAsPermission.Core) super.runAs();
}
public static Builder builder(String name) {
return new Builder(name);
}
public static Builder builder(RoleDescriptor rd) {
return new Builder(rd);
}
public static class Builder {
private final String name;
private ClusterPermission.Core cluster = ClusterPermission.Core.NONE;
private RunAsPermission.Core runAs = RunAsPermission.Core.NONE;
private List<IndicesPermission.Group> groups = new ArrayList<>();
private Builder(String name) {
this.name = name;
}
private Builder(RoleDescriptor rd) {
this.name = rd.getName();
this.cluster(ClusterPrivilege.get((new Privilege.Name(rd.getClusterPattern()))));
for (RoleDescriptor.IndicesPrivileges iGroup : rd.getIndicesPrivileges()) {
this.add(iGroup.getFields() == null ? null : Arrays.asList(iGroup.getFields()),
iGroup.getQuery(),
IndexPrivilege.get(new Privilege.Name(iGroup.getPrivileges())),
iGroup.getIndices());
}
String[] rdRunAs = rd.getRunAs();
if (rdRunAs != null && rdRunAs.length > 0) {
this.runAs(new GeneralPrivilege(new Privilege.Name(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);
return this;
}
public Builder runAs(GeneralPrivilege privilege) {
runAs = new RunAsPermission.Core(privilege);
return this;
}
public Builder add(IndexPrivilege privilege, String... indices) {
groups.add(new IndicesPermission.Group(privilege, null, null, indices));
return this;
}
public Builder add(List<String> fields, BytesReference query, IndexPrivilege privilege, String... indices) {
groups.add(new IndicesPermission.Group(privilege, fields, 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()]));
return new Role(name, cluster, indices, runAs);
}
}
}

View File

@ -0,0 +1,80 @@
/*
* 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.shield.authz.permission;
import org.elasticsearch.shield.authz.privilege.GeneralPrivilege;
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 {
/**
* 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;
}
}
}

View File

@ -0,0 +1,80 @@
/*
* 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.shield.authz.privilege;
import dk.brics.automaton.Automaton;
import dk.brics.automaton.BasicOperations;
import org.elasticsearch.shield.support.AutomatonPredicate;
import org.elasticsearch.shield.support.Automatons;
import java.util.function.Predicate;
import static org.elasticsearch.shield.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));
}
protected P minus(P other) {
if (other.implies((P) this)) {
return none();
}
if (other == none() || !this.implies(other)) {
return (P) this;
}
return create(name.remove(other.name), Automatons.minusAndDeterminize(automaton, other.automaton));
}
@Override
public boolean implies(P other) {
return BasicOperations.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

@ -0,0 +1,109 @@
/*
* 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.shield.authz.privilege;
import dk.brics.automaton.Automaton;
import dk.brics.automaton.BasicAutomata;
import org.elasticsearch.common.Strings;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Predicate;
/**
*
*/
public class ClusterPrivilege extends AbstractAutomatonPrivilege<ClusterPrivilege> {
public static final ClusterPrivilege NONE = new ClusterPrivilege(Name.NONE, BasicAutomata.makeEmpty());
public static final ClusterPrivilege ALL = new ClusterPrivilege(Name.ALL, "cluster:*", "indices:admin/template/*");
public static final ClusterPrivilege MONITOR = new ClusterPrivilege("monitor", "cluster:monitor/*");
public static final ClusterPrivilege MANAGE_SHIELD = new ClusterPrivilege("manage_shield", "cluster:admin/shield/*");
public final static Predicate<String> ACTION_MATCHER = ClusterPrivilege.ALL.predicate();
private static final Set<ClusterPrivilege> values = new CopyOnWriteArraySet<>();
static {
values.add(NONE);
values.add(ALL);
values.add(MONITOR);
values.add(MANAGE_SHIELD);
}
static Set<ClusterPrivilege> values() {
return values;
}
private static final ConcurrentHashMap<Name, ClusterPrivilege> cache = new ConcurrentHashMap<>();
private ClusterPrivilege(String name, String... patterns) {
super(name, patterns);
}
private ClusterPrivilege(Name name, String... patterns) {
super(name, patterns);
}
private ClusterPrivilege(Name 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");
}
}
ClusterPrivilege custom = new ClusterPrivilege(name, actionPatterns);
if (values.contains(custom)) {
throw new IllegalArgumentException("cannot register custom cluster privilege [" + name + "] as it already exists.");
}
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");
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.shield.authz.privilege;
import dk.brics.automaton.Automaton;
import dk.brics.automaton.BasicAutomata;
/**
*
*/
public class GeneralPrivilege extends AbstractAutomatonPrivilege<GeneralPrivilege> {
public static final GeneralPrivilege NONE = new GeneralPrivilege(Name.NONE, BasicAutomata.makeEmpty());
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

@ -0,0 +1,23 @@
/*
* 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.shield.authz.privilege;
/**
*
*/
public class HealthAndStatsPrivilege extends GeneralPrivilege {
public static final HealthAndStatsPrivilege INSTANCE = new HealthAndStatsPrivilege();
public static final String NAME = "health_and_stats";
private HealthAndStatsPrivilege() {
super(NAME, "cluster:monitor/health*",
"cluster:monitor/stats*",
"indices:monitor/stats*",
"cluster:monitor/nodes/stats*");
}
}

View File

@ -0,0 +1,149 @@
/*
* 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.shield.authz.privilege;
import dk.brics.automaton.Automaton;
import dk.brics.automaton.BasicAutomata;
import org.elasticsearch.action.admin.indices.create.CreateIndexAction;
import org.elasticsearch.action.get.GetAction;
import org.elasticsearch.action.get.MultiGetAction;
import org.elasticsearch.action.search.MultiSearchAction;
import org.elasticsearch.action.search.SearchAction;
import org.elasticsearch.action.suggest.SuggestAction;
import org.elasticsearch.common.Strings;
import java.util.Locale;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Predicate;
/**
*
*/
public class IndexPrivilege extends AbstractAutomatonPrivilege<IndexPrivilege> {
public static final IndexPrivilege NONE = new IndexPrivilege(Name.NONE, BasicAutomata.makeEmpty());
public static final IndexPrivilege ALL = new IndexPrivilege(Name.ALL, "indices:*");
public static final IndexPrivilege MANAGE = new IndexPrivilege("manage", "indices:monitor/*", "indices:admin/*");
public static final IndexPrivilege CREATE_INDEX = new IndexPrivilege("create_index", CreateIndexAction.NAME);
public static final IndexPrivilege MANAGE_ALIASES = new IndexPrivilege("manage_aliases", "indices:admin/aliases*");
public static final IndexPrivilege MONITOR = new IndexPrivilege("monitor", "indices:monitor/*");
public static final IndexPrivilege DATA_ACCESS = new IndexPrivilege("data_access", "indices:data/*");
public static final IndexPrivilege CRUD = new IndexPrivilege("crud", "indices:data/write/*", "indices:data/read/*");
public static final IndexPrivilege READ = new IndexPrivilege("read", "indices:data/read/*");
public static final IndexPrivilege SEARCH = new IndexPrivilege("search", SearchAction.NAME + "*", MultiSearchAction.NAME + "*", SuggestAction.NAME + "*");
public static final IndexPrivilege GET = new IndexPrivilege("get", GetAction.NAME + "*", MultiGetAction.NAME + "*");
public static final IndexPrivilege SUGGEST = new IndexPrivilege("suggest", SuggestAction.NAME + "*");
public static final IndexPrivilege INDEX = new IndexPrivilege("index", "indices:data/write/index*", "indices:data/write/update*");
public static final IndexPrivilege DELETE = new IndexPrivilege("delete", "indices:data/write/delete*");
public static final IndexPrivilege WRITE = new IndexPrivilege("write", "indices:data/write/*");
private static final Set<IndexPrivilege> values = new CopyOnWriteArraySet<>();
static {
values.add(NONE);
values.add(ALL);
values.add(MANAGE);
values.add(CREATE_INDEX);
values.add(MANAGE_ALIASES);
values.add(MONITOR);
values.add(DATA_ACCESS);
values.add(CRUD);
values.add(READ);
values.add(SEARCH);
values.add(GET);
values.add(SUGGEST);
values.add(INDEX);
values.add(DELETE);
values.add(WRITE);
}
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 IndexPrivilege(Name name, String... patterns) {
super(name, patterns);
}
private IndexPrivilege(Name 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");
}
}
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);
}
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;
}
}
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");
}
}

View File

@ -0,0 +1,114 @@
/*
* 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.shield.authz.privilege;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.util.set.Sets;
import java.util.HashSet;
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;
/**
*
*/
public abstract class Privilege<P extends Privilege<P>> {
protected final Name name;
Privilege(Name name) {
this.name = name;
}
public Name 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);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Privilege privilege = (Privilege) o;
if (name != null ? !name.equals(privilege.name) : privilege.name != null) return false;
return true;
}
@Override
public int hashCode() {
return name != null ? name.hashCode() : 0;
}
static String actionToPattern(String text) {
return text + "*";
}
public static class Name {
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();
}
}
}

View File

@ -0,0 +1,42 @@
/*
* 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.shield.authz.privilege;
import org.elasticsearch.shield.support.AutomatonPredicate;
import java.util.function.Predicate;
import static org.elasticsearch.shield.support.Automatons.patterns;
/**
*
*/
public class SystemPrivilege extends Privilege<SystemPrivilege> {
public static SystemPrivilege INSTANCE = new SystemPrivilege();
protected static final Predicate<String> PREDICATE = new AutomatonPredicate(patterns(
"internal:*",
"indices:monitor/*", // added for marvel
"cluster:monitor/*", // added for marvel
"cluster:admin/reroute", // added for DiskThresholdDecider.DiskListener
"indices:admin/mapping/put" // ES 2.0 MappingUpdatedAction - updateMappingOnMasterSynchronously
));
SystemPrivilege() {
super(new Name("internal"));
}
@Override
public Predicate<String> predicate() {
return PREDICATE;
}
@Override
public boolean implies(SystemPrivilege other) {
return true;
}
}

View File

@ -6,9 +6,8 @@
package org.elasticsearch.shield.authz.store;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.shield.authz.Permission;
import org.elasticsearch.shield.authz.RoleDescriptor;
import org.elasticsearch.shield.authz.esnative.ESNativeRolesStore;
import org.elasticsearch.shield.authz.permission.Role;
/**
* A composite roles store that combines file-based and index-based roles
@ -25,9 +24,9 @@ public class CompositeRolesStore implements RolesStore {
this.nativeRolesStore = nativeRolesStore;
}
public Permission.Global.Role role(String role) {
public Role role(String role) {
// Try the file first, then the index if it isn't there
Permission.Global.Role fileRole = fileRolesStore.role(role);
Role fileRole = fileRolesStore.role(role);
if (fileRole != null) {
return fileRole;
}

View File

@ -22,9 +22,11 @@ import org.elasticsearch.common.xcontent.yaml.YamlXContent;
import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.ShieldPlugin;
import org.elasticsearch.shield.authc.support.RefreshListener;
import org.elasticsearch.shield.authz.Permission;
import org.elasticsearch.shield.authz.Privilege;
import org.elasticsearch.shield.authz.RoleDescriptor;
import org.elasticsearch.shield.authz.permission.Role;
import org.elasticsearch.shield.authz.privilege.ClusterPrivilege;
import org.elasticsearch.shield.authz.privilege.GeneralPrivilege;
import org.elasticsearch.shield.authz.privilege.IndexPrivilege;
import org.elasticsearch.shield.authz.privilege.Privilege;
import org.elasticsearch.shield.authz.SystemRole;
import org.elasticsearch.shield.support.NoOpLogger;
import org.elasticsearch.shield.support.Validation;
@ -62,17 +64,17 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
private final Path file;
private final RefreshListener listener;
private final Set<Permission.Global.Role> reservedRoles;
private final Set<Role> reservedRoles;
private final ResourceWatcherService watcherService;
private volatile Map<String, Permission.Global.Role> permissions;
private volatile Map<String, Role> permissions;
@Inject
public FileRolesStore(Settings settings, Environment env, ResourceWatcherService watcherService, Set<Permission.Global.Role> reservedRoles) {
public FileRolesStore(Settings settings, Environment env, ResourceWatcherService watcherService, Set<Role> reservedRoles) {
this(settings, env, watcherService, reservedRoles, RefreshListener.NOOP);
}
public FileRolesStore(Settings settings, Environment env, ResourceWatcherService watcherService, Set<Permission.Global.Role> reservedRoles, RefreshListener listener) {
public FileRolesStore(Settings settings, Environment env, ResourceWatcherService watcherService, Set<Role> reservedRoles, RefreshListener listener) {
super(settings);
this.file = resolveFile(settings, env);
this.listener = listener;
@ -102,7 +104,7 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
}
@Override
public Permission.Global.Role role(String role) {
public Role role(String role) {
return permissions.get(role);
}
@ -116,29 +118,29 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
}
public static Set<String> parseFileForRoleNames(Path path, ESLogger logger) {
Map<String, Permission.Global.Role> roleMap = parseFile(path, Collections.<Permission.Global.Role>emptySet(), logger, false, Settings.EMPTY);
Map<String, Role> roleMap = parseFile(path, Collections.<Role>emptySet(), logger, false, Settings.EMPTY);
if (roleMap == null) {
return emptySet();
}
return roleMap.keySet();
}
public static Map<String, Permission.Global.Role> parseFile(Path path, Set<Permission.Global.Role> reservedRoles, ESLogger logger, Settings settings) {
public static Map<String, Role> parseFile(Path path, Set<Role> reservedRoles, ESLogger logger, Settings settings) {
return parseFile(path, reservedRoles, logger, true, settings);
}
public static Map<String, Permission.Global.Role> parseFile(Path path, Set<Permission.Global.Role> reservedRoles, ESLogger logger, boolean resolvePermission, Settings settings) {
public static Map<String, Role> parseFile(Path path, Set<Role> reservedRoles, ESLogger logger, boolean resolvePermission, Settings settings) {
if (logger == null) {
logger = NoOpLogger.INSTANCE;
}
Map<String, Permission.Global.Role> roles = new HashMap<>();
Map<String, Role> roles = new HashMap<>();
logger.trace("attempted to read roles file located at [{}]", path.toAbsolutePath());
if (Files.exists(path)) {
try {
List<String> roleSegments = roleSegments(path);
for (String segment : roleSegments) {
Permission.Global.Role role = parseRole(segment, path, logger, resolvePermission, settings);
Role role = parseRole(segment, path, logger, resolvePermission, settings);
if (role != null) {
if (SystemRole.NAME.equals(role.name())) {
logger.warn("role [{}] is reserved to the system. the relevant role definition in the mapping file will be ignored", SystemRole.NAME);
@ -154,7 +156,7 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
}
// we now add all the fixed roles (overriding any attempts to override the fixed roles in the file)
for (Permission.Global.Role reservedRole : reservedRoles) {
for (Role reservedRole : reservedRoles) {
if (roles.containsKey(reservedRole.name())) {
logger.warn("role [{}] is reserved to the system. the relevant role definition in the mapping file will be ignored", reservedRole.name());
}
@ -164,7 +166,7 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
return unmodifiableMap(roles);
}
private static Permission.Global.Role parseRole(String segment, Path path, ESLogger logger, boolean resolvePermissions, Settings settings) {
private static Role parseRole(String segment, Path path, ESLogger logger, boolean resolvePermissions, Settings settings) {
String roleName = null;
try {
XContentParser parser = YamlXContent.yamlXContent.createParser(segment);
@ -179,9 +181,9 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
return null;
}
Permission.Global.Role.Builder permission = Permission.Global.Role.builder(roleName);
Role.Builder role = Role.builder(roleName);
if (resolvePermissions == false) {
return permission.build();
return role.build();
}
token = parser.nextToken();
@ -216,7 +218,7 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
}
if (name != null) {
try {
permission.cluster(Privilege.Cluster.get(name));
role.cluster(ClusterPrivilege.get(name));
} catch (IllegalArgumentException e) {
logger.error("invalid role definition [{}] in roles file [{}]. could not resolve cluster privileges [{}]. skipping role...", roleName, path.toAbsolutePath(), name);
return null;
@ -307,7 +309,7 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
}
try {
permission.add(fields, query, Privilege.Index.get(name), indices);
role.add(fields, query, IndexPrivilege.get(name), indices);
} catch (IllegalArgumentException e) {
logger.error("invalid role definition [{}] in roles file [{}]. could not resolve indices privileges [{}]. skipping role...", roleName, path.toAbsolutePath(), name);
return null;
@ -321,7 +323,7 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
}
if (name != null) {
try {
permission.add(Privilege.Index.get(name), indices);
role.add(IndexPrivilege.get(name), indices);
} catch (IllegalArgumentException e) {
logger.error("invalid role definition [{}] in roles file [{}]. could not resolve indices privileges [{}]. skipping role...", roleName, path.toAbsolutePath(), name);
return null;
@ -358,7 +360,7 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
if (!names.isEmpty()) {
Privilege.Name name = new Privilege.Name(names);
try {
permission.runAs(new Privilege.General(new Privilege.Name(names), names.toArray(new String[names.size()])));
role.runAs(new GeneralPrivilege(new Privilege.Name(names), names.toArray(new String[names.size()])));
} catch (IllegalArgumentException e) {
logger.error("invalid role definition [{}] in roles file [{}]. could not resolve run_as privileges [{}]. skipping role...", roleName, path.toAbsolutePath(), name);
return null;
@ -368,7 +370,7 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
logger.warn("unknown field [{}] found in role definition [{}] in roles file [{}]", currentFieldName, roleName, path.toAbsolutePath());
}
}
return permission.build();
return role.build();
}
logger.error("invalid role definition [{}] in roles file [{}]. skipping role...", roleName, path.toAbsolutePath());
}

View File

@ -5,14 +5,13 @@
*/
package org.elasticsearch.shield.authz.store;
import org.elasticsearch.shield.authz.Permission;
import org.elasticsearch.shield.authz.RoleDescriptor;
import org.elasticsearch.shield.authz.permission.Role;
/**
* An interface for looking up a role given a string role name
*/
public interface RolesStore {
Permission.Global.Role role(String role);
Role role(String role);
}

View File

@ -12,7 +12,6 @@ import org.elasticsearch.common.Strings;
import org.elasticsearch.common.network.NetworkModule;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.node.Node;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.shield.action.admin.role.AddRoleResponse;
import org.elasticsearch.shield.action.admin.role.GetRolesResponse;
@ -42,6 +41,7 @@ import static org.hamcrest.Matchers.notNullValue;
* Test for the Shield clear roles API that changes the polling aspect of shield to only run once an hour in order to
* test the cache clearing APIs.
*/
@TestLogging("shield.authc.esnative:TRACE,shield.authz.esnative:TRACE,integration:DEBUG")
public class ClearRolesCacheTests extends ShieldIntegTestCase {
private static String[] roles;
@ -85,6 +85,7 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase {
.cluster("none")
.addIndices(new String[] { "*" }, new String[] { "ALL" }, null, null)
.get();
logger.debug("--> created role [{}]", role);
}
ensureYellow(ShieldTemplateService.SHIELD_ADMIN_INDEX_NAME);
@ -106,13 +107,13 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase {
.build();
}
@TestLogging("_root:DEBUG")
public void testModifyingViaApiClearsCache() throws Exception {
Client client = internalCluster().transportClient();
ShieldClient shieldClient = new ShieldClient(client);
int modifiedRolesCount = randomIntBetween(1, roles.length);
List<String> toModify = randomSubsetOf(modifiedRolesCount, roles);
logger.debug("--> modifying roles {} to have run_as", toModify);
for (String role : toModify) {
AddRoleResponse response = shieldClient.prepareAddRole().name(role)
.cluster("none")
@ -120,6 +121,7 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase {
.runAs(role)
.get();
assertThat(response.isCreated(), is(false));
logger.debug("--> updated role [{}] with run_as", role);
}
assertRolesAreCorrect(shieldClient, toModify);
@ -130,12 +132,14 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase {
int modifiedRolesCount = randomIntBetween(1, roles.length);
List<String> toModify = randomSubsetOf(modifiedRolesCount, roles);
logger.debug("--> modifying roles {} to have run_as", toModify);
for (String role : toModify) {
UpdateResponse response = client.prepareUpdate().setId(role).setIndex(ShieldTemplateService.SHIELD_ADMIN_INDEX_NAME)
.setType(ESNativeRolesStore.INDEX_ROLE_TYPE)
.setDoc("run_as", new String[] { role })
.get();
assertThat(response.isCreated(), is(false));
logger.debug("--> updated role [{}] with run_as", role);
}
ShieldClient shieldClient = new ShieldClient(client);
@ -143,11 +147,12 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase {
GetRolesResponse roleResponse = shieldClient.prepareGetRoles().roles(role).get();
assertThat(roleResponse.isExists(), is(true));
final String[] runAs = roleResponse.roles().get(0).getRunAs();
assertThat("role should be cached and no rules have run as set", runAs == null || runAs.length == 0, is(true));
assertThat("role [" + role + "] should be cached and no rules have run as set", runAs == null || runAs.length == 0, is(true));
}
boolean useHttp = randomBoolean();
boolean clearAll = randomBoolean();
final boolean useHttp = randomBoolean();
final boolean clearAll = randomBoolean();
logger.debug("--> starting to clear roles. using http [{}] clearing all [{}]", useHttp, clearAll);
String[] rolesToClear = clearAll ? (randomBoolean() ? roles : null) : toModify.toArray(Strings.EMPTY_ARRAY);
if (useHttp) {
String path;
@ -176,6 +181,7 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase {
final String role = randomFrom(roles);
List<RoleDescriptor> foundRoles = shieldClient.prepareGetRoles().roles(role).get().roles();
assertThat(foundRoles.size(), is(1));
logger.debug("--> deleting role [{}]", role);
DeleteResponse response = client.prepareDelete(ShieldTemplateService.SHIELD_ADMIN_INDEX_NAME, ESNativeRolesStore.INDEX_ROLE_TYPE, role).get();
assertThat(response.isFound(), is(true));
@ -189,14 +195,15 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase {
private void assertRolesAreCorrect(ShieldClient shieldClient, List<String> toModify) {
for (String role : roles) {
logger.debug("--> getting role [{}]", role);
GetRolesResponse roleResponse = shieldClient.prepareGetRoles().roles(role).get();
assertThat(roleResponse.isExists(), is(true));
final String[] runAs = roleResponse.roles().get(0).getRunAs();
if (toModify.contains(role)) {
assertThat("role should be modified and have run as", runAs == null || runAs.length == 0, is(false));
assertThat("role [" + role + "] should be modified and have run as", runAs == null || runAs.length == 0, is(false));
assertThat(Arrays.asList(runAs).contains(role), is(true));
} else {
assertThat("role should be cached and no rules have run as set", runAs == null || runAs.length == 0, is(true));
assertThat("role [" + role + "] should be cached and not have run as set but does!", runAs == null || runAs.length == 0, is(true));
}
}
}

View File

@ -19,9 +19,9 @@ import org.elasticsearch.shield.action.admin.user.DeleteUserResponse;
import org.elasticsearch.shield.action.admin.user.GetUsersResponse;
import org.elasticsearch.shield.authc.esnative.ESNativeUsersStore;
import org.elasticsearch.shield.authc.support.SecuredString;
import org.elasticsearch.shield.authz.Permission;
import org.elasticsearch.shield.authz.RoleDescriptor;
import org.elasticsearch.shield.authz.esnative.ESNativeRolesStore;
import org.elasticsearch.shield.authz.permission.Role;
import org.elasticsearch.shield.client.ShieldClient;
import org.elasticsearch.test.ShieldIntegTestCase;
import org.elasticsearch.test.ShieldSettingsSource;
@ -322,7 +322,7 @@ public class ESNativeTests extends ShieldIntegTestCase {
GetRolesResponse getRolesResponse = c.prepareGetRoles().roles("test_role").get();
assertTrue("test_role does not exist!", getRolesResponse.isExists());
assertTrue("any cluster permission should be authorized",
Permission.Global.Role.builder(getRolesResponse.roles().get(0)).build().cluster().check("cluster:admin/foo"));
Role.builder(getRolesResponse.roles().get(0)).build().cluster().check("cluster:admin/foo"));
c.prepareAddRole()
.name("test_role")
.cluster("none")
@ -333,7 +333,7 @@ public class ESNativeTests extends ShieldIntegTestCase {
assertTrue("test_role does not exist!", getRolesResponse.isExists());
assertFalse("no cluster permission should be authorized",
Permission.Global.Role.builder(getRolesResponse.roles().get(0)).build().cluster().check("cluster:admin/bar"));
Role.builder(getRolesResponse.roles().get(0)).build().cluster().check("cluster:admin/bar"));
}
}

View File

@ -28,6 +28,10 @@ import org.elasticsearch.shield.User;
import org.elasticsearch.shield.audit.AuditTrail;
import org.elasticsearch.shield.authc.AnonymousService;
import org.elasticsearch.shield.authc.DefaultAuthenticationFailureHandler;
import org.elasticsearch.shield.authz.permission.Role;
import org.elasticsearch.shield.authz.privilege.ClusterPrivilege;
import org.elasticsearch.shield.authz.privilege.GeneralPrivilege;
import org.elasticsearch.shield.authz.privilege.IndexPrivilege;
import org.elasticsearch.shield.authz.store.RolesStore;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.transport.TransportRequest;
@ -139,7 +143,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
public void testThatNonIndicesAndNonClusterActionIsDenied() {
TransportRequest request = mock(TransportRequest.class);
User user = new User("test user", "a_all");
when(rolesStore.role("a_all")).thenReturn(Permission.Global.Role.builder("a_role").add(Privilege.Index.ALL, "a").build());
when(rolesStore.role("a_all")).thenReturn(Role.builder("a_role").add(IndexPrivilege.ALL, "a").build());
try {
internalAuthorizationService.authorize(user, "whatever", request);
@ -154,7 +158,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
public void testThatRoleWithNoIndicesIsDenied() {
TransportRequest request = new IndicesExistsRequest("a");
User user = new User("test user", "no_indices");
when(rolesStore.role("no_indices")).thenReturn(Permission.Global.Role.builder("no_indices").cluster(Privilege.Cluster.action("")).build());
when(rolesStore.role("no_indices")).thenReturn(Role.builder("no_indices").cluster(ClusterPrivilege.action("")).build());
try {
internalAuthorizationService.authorize(user, "indices:a", request);
@ -168,7 +172,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
public void testScrollRelatedRequestsAllowed() {
User user = new User("test user", "a_all");
when(rolesStore.role("a_all")).thenReturn(Permission.Global.Role.builder("a_role").add(Privilege.Index.ALL, "a").build());
when(rolesStore.role("a_all")).thenReturn(Role.builder("a_role").add(IndexPrivilege.ALL, "a").build());
ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
internalAuthorizationService.authorize(user, ClearScrollAction.NAME, clearScrollRequest);
@ -201,7 +205,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
TransportRequest request = new IndicesExistsRequest("b");
ClusterState state = mock(ClusterState.class);
User user = new User("test user", "a_all");
when(rolesStore.role("a_all")).thenReturn(Permission.Global.Role.builder("a_all").add(Privilege.Index.ALL, "a").build());
when(rolesStore.role("a_all")).thenReturn(Role.builder("a_all").add(IndexPrivilege.ALL, "a").build());
when(clusterService.state()).thenReturn(state);
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
@ -222,7 +226,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
request.alias(new Alias("a2"));
ClusterState state = mock(ClusterState.class);
User user = new User("test user", "a_all");
when(rolesStore.role("a_all")).thenReturn(Permission.Global.Role.builder("a_all").add(Privilege.Index.ALL, "a").build());
when(rolesStore.role("a_all")).thenReturn(Role.builder("a_all").add(IndexPrivilege.ALL, "a").build());
when(clusterService.state()).thenReturn(state);
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
@ -243,7 +247,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
request.alias(new Alias("a2"));
ClusterState state = mock(ClusterState.class);
User user = new User("test user", "a_all");
when(rolesStore.role("a_all")).thenReturn(Permission.Global.Role.builder("a_all").add(Privilege.Index.ALL, "a", "a2").build());
when(rolesStore.role("a_all")).thenReturn(Role.builder("a_all").add(IndexPrivilege.ALL, "a", "a2").build());
when(clusterService.state()).thenReturn(state);
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
@ -265,8 +269,8 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
public void testIndicesAliasesWithUserHavingRoles() {
User user = new User("test user", "a_star", "b");
ClusterState state = mock(ClusterState.class);
when(rolesStore.role("a_star")).thenReturn(Permission.Global.Role.builder("a_star").add(Privilege.Index.ALL, "a*").build());
when(rolesStore.role("b")).thenReturn(Permission.Global.Role.builder("a_star").add(Privilege.Index.SEARCH, "b").build());
when(rolesStore.role("a_star")).thenReturn(Role.builder("a_star").add(IndexPrivilege.ALL, "a*").build());
when(rolesStore.role("b")).thenReturn(Role.builder("a_star").add(IndexPrivilege.SEARCH, "b").build());
when(clusterService.state()).thenReturn(state);
Settings indexSettings = Settings.builder().put("index.version.created", Version.CURRENT).build();
when(state.metaData()).thenReturn(MetaData.builder()
@ -295,7 +299,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
AnonymousService anonymousService = new AnonymousService(Settings.builder().put("shield.authc.anonymous.roles", "a_all").build());
internalAuthorizationService = new InternalAuthorizationService(Settings.EMPTY, rolesStore, clusterService, auditTrail, anonymousService, new DefaultAuthenticationFailureHandler());
when(rolesStore.role("a_all")).thenReturn(Permission.Global.Role.builder("a_all").add(Privilege.Index.ALL, "a").build());
when(rolesStore.role("a_all")).thenReturn(Role.builder("a_all").add(IndexPrivilege.ALL, "a").build());
when(clusterService.state()).thenReturn(state);
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
@ -320,7 +324,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
.build());
internalAuthorizationService = new InternalAuthorizationService(Settings.EMPTY, rolesStore, clusterService, auditTrail, anonymousService, new DefaultAuthenticationFailureHandler());
when(rolesStore.role("a_all")).thenReturn(Permission.Global.Role.builder("a_all").add(Privilege.Index.ALL, "a").build());
when(rolesStore.role("a_all")).thenReturn(Role.builder("a_all").add(IndexPrivilege.ALL, "a").build());
when(clusterService.state()).thenReturn(state);
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
@ -354,10 +358,10 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
TransportRequest request = mock(TransportRequest.class);
User user = new User("test user", new String[] { "can run as" }, new User("run as me", new String[] { "doesn't exist" }));
assertThat(user.runAs(), is(notNullValue()));
when(rolesStore.role("can run as")).thenReturn(Permission.Global.Role
when(rolesStore.role("can run as")).thenReturn(Role
.builder("can run as")
.runAs(new Privilege.General("", "not the right user"))
.add(Privilege.Index.ALL, "a")
.runAs(new GeneralPrivilege("", "not the right user"))
.add(IndexPrivilege.ALL, "a")
.build());
try {
@ -374,10 +378,10 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
TransportRequest request = new IndicesExistsRequest("a");
User user = new User("test user", new String[] { "can run as" }, new User("run as me", new String[] { "b" }));
assertThat(user.runAs(), is(notNullValue()));
when(rolesStore.role("can run as")).thenReturn(Permission.Global.Role
when(rolesStore.role("can run as")).thenReturn(Role
.builder("can run as")
.runAs(new Privilege.General("", "run as me"))
.add(Privilege.Index.ALL, "a")
.runAs(new GeneralPrivilege("", "run as me"))
.add(IndexPrivilege.ALL, "a")
.build());
if (randomBoolean()) {
@ -388,9 +392,9 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
.settings(Settings.builder().put("index.version.created", Version.CURRENT).build())
.numberOfShards(1).numberOfReplicas(0).build(), true)
.build());
when(rolesStore.role("b")).thenReturn(Permission.Global.Role
when(rolesStore.role("b")).thenReturn(Role
.builder("b")
.add(Privilege.Index.ALL, "b")
.add(IndexPrivilege.ALL, "b")
.build());
}
@ -409,10 +413,10 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
TransportRequest request = new IndicesExistsRequest("b");
User user = new User("test user", new String[] { "can run as" }, new User("run as me", new String[] { "b" }));
assertThat(user.runAs(), is(notNullValue()));
when(rolesStore.role("can run as")).thenReturn(Permission.Global.Role
when(rolesStore.role("can run as")).thenReturn(Role
.builder("can run as")
.runAs(new Privilege.General("", "run as me"))
.add(Privilege.Index.ALL, "a")
.runAs(new GeneralPrivilege("", "run as me"))
.add(IndexPrivilege.ALL, "a")
.build());
ClusterState state = mock(ClusterState.class);
when(clusterService.state()).thenReturn(state);
@ -421,9 +425,9 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
.settings(Settings.builder().put("index.version.created", Version.CURRENT).build())
.numberOfShards(1).numberOfReplicas(0).build(), true)
.build());
when(rolesStore.role("b")).thenReturn(Permission.Global.Role
when(rolesStore.role("b")).thenReturn(Role
.builder("b")
.add(Privilege.Index.ALL, "b")
.add(IndexPrivilege.ALL, "b")
.build());
internalAuthorizationService.authorize(user, "indices:a", request);

View File

@ -14,8 +14,8 @@ import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.set.Sets;
import org.elasticsearch.shield.authz.Permission;
import org.elasticsearch.shield.authz.Privilege;
import org.elasticsearch.shield.authz.permission.Role;
import org.elasticsearch.shield.authz.privilege.IndexPrivilege;
import org.elasticsearch.test.ESTestCase;
import java.util.Arrays;
@ -40,7 +40,7 @@ public class IndicesPermissionTests extends ESTestCase {
// basics:
BytesReference query = new BytesArray("{}");
List<String> fields = Arrays.asList("_field");
Permission.Global.Role role = Permission.Global.Role.builder("_role").add(fields, query, Privilege.Index.ALL, "_index").build();
Role role = Role.builder("_role").add(fields, query, IndexPrivilege.ALL, "_index").build();
IndicesAccessControl permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md);
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
assertThat(permissions.getIndexPermissions("_index").getFields().size(), equalTo(1));
@ -49,7 +49,7 @@ public class IndicesPermissionTests extends ESTestCase {
assertThat(permissions.getIndexPermissions("_index").getQueries().iterator().next(), equalTo(query));
// no document level security:
role = Permission.Global.Role.builder("_role").add(fields, null, Privilege.Index.ALL, "_index").build();
role = Role.builder("_role").add(fields, null, IndexPrivilege.ALL, "_index").build();
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md);
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
assertThat(permissions.getIndexPermissions("_index").getFields().size(), equalTo(1));
@ -57,7 +57,7 @@ public class IndicesPermissionTests extends ESTestCase {
assertThat(permissions.getIndexPermissions("_index").getQueries(), nullValue());
// no field level security:
role = Permission.Global.Role.builder("_role").add(null, query, Privilege.Index.ALL, "_index").build();
role = Role.builder("_role").add(null, query, IndexPrivilege.ALL, "_index").build();
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md);
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
assertThat(permissions.getIndexPermissions("_index").getFields(), nullValue());
@ -65,7 +65,7 @@ public class IndicesPermissionTests extends ESTestCase {
assertThat(permissions.getIndexPermissions("_index").getQueries().iterator().next(), equalTo(query));
// index group associated with an alias:
role = Permission.Global.Role.builder("_role").add(fields, query, Privilege.Index.ALL, "_alias").build();
role = Role.builder("_role").add(fields, query, IndexPrivilege.ALL, "_alias").build();
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_alias"), md);
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
assertThat(permissions.getIndexPermissions("_index").getFields().size(), equalTo(1));

View File

@ -3,10 +3,11 @@
* 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.shield.authz;
package org.elasticsearch.shield.authz.permission;
import org.elasticsearch.action.get.GetAction;
import org.elasticsearch.shield.authz.Privilege.Cluster;
import org.elasticsearch.shield.authz.privilege.GeneralPrivilege;
import org.elasticsearch.shield.authz.privilege.ClusterPrivilege;
import org.elasticsearch.test.ESTestCase;
import org.junit.Before;
@ -15,10 +16,10 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.function.Predicate;
import static org.elasticsearch.shield.authz.Privilege.Index.MONITOR;
import static org.elasticsearch.shield.authz.Privilege.Index.READ;
import static org.elasticsearch.shield.authz.Privilege.Index.SEARCH;
import static org.elasticsearch.shield.authz.Privilege.Index.union;
import static org.elasticsearch.shield.authz.privilege.IndexPrivilege.MONITOR;
import static org.elasticsearch.shield.authz.privilege.IndexPrivilege.READ;
import static org.elasticsearch.shield.authz.privilege.IndexPrivilege.SEARCH;
import static org.elasticsearch.shield.authz.privilege.IndexPrivilege.union;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
@ -27,11 +28,11 @@ import static org.hamcrest.Matchers.notNullValue;
*
*/
public class PermissionTests extends ESTestCase {
private Permission.Global.Role permission;
private Role permission;
@Before
public void init() {
Permission.Global.Role.Builder builder = Permission.Global.Role.builder("test");
Role.Builder builder = Role.builder("test");
builder.add(union(SEARCH, MONITOR), "test_*", "/foo.*/");
builder.add(union(READ), "baz_*foo", "/fool.*bar/");
builder.add(union(MONITOR), "/bar.*/");
@ -49,12 +50,12 @@ public class PermissionTests extends ESTestCase {
}
public void testIndicesGlobalsIterator() {
Permission.Global.Role.Builder builder = Permission.Global.Role.builder("tc_role");
builder.cluster(Cluster.action("cluster:monitor/nodes/info"));
Permission.Global.Role noIndicesPermission = builder.build();
Role.Builder builder = Role.builder("tc_role");
builder.cluster(ClusterPrivilege.action("cluster:monitor/nodes/info"));
Role noIndicesPermission = builder.build();
Permission.Indices.Globals indicesGlobals = new Permission.Indices.Globals(Collections.<Permission.Global>unmodifiableList(Arrays.asList(noIndicesPermission, permission)));
Iterator<Permission.Indices.Group> iterator = indicesGlobals.iterator();
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()) {
@ -65,8 +66,8 @@ public class PermissionTests extends ESTestCase {
}
public void testBuildEmptyRole() {
Permission.Global.Role.Builder permission = Permission.Global.Role.builder("some_role");
Permission.Global.Role role = permission.build();
Role.Builder permission = Role.builder("some_role");
Role role = permission.build();
assertThat(role, notNullValue());
assertThat(role.cluster(), notNullValue());
assertThat(role.indices(), notNullValue());
@ -74,8 +75,8 @@ public class PermissionTests extends ESTestCase {
}
public void testRunAs() {
Permission.Global.Role permission = Permission.Global.Role.builder("some_role")
.runAs(new Privilege.General("name", "user1", "run*"))
Role permission = Role.builder("some_role")
.runAs(new GeneralPrivilege("name", "user1", "run*"))
.build();
assertThat(permission.runAs().check("user1"), is(true));
assertThat(permission.runAs().check("user"), is(false));

View File

@ -3,7 +3,7 @@
* 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.shield.authz;
package org.elasticsearch.shield.authz.privilege;
import org.elasticsearch.action.get.GetAction;
import org.elasticsearch.action.get.MultiGetAction;
@ -47,7 +47,7 @@ public class PrivilegeTests extends ESTestCase {
}
public void testSubActionPattern() throws Exception {
AutomatonPredicate predicate = new AutomatonPredicate(Automatons.patterns("foo" + Privilege.SUB_ACTION_SUFFIX_PATTERN));
AutomatonPredicate predicate = new AutomatonPredicate(Automatons.patterns("foo*"));
assertThat(predicate.test("foo[n][nodes]"), is(true));
assertThat(predicate.test("foo[n]"), is(true));
assertThat(predicate.test("bar[n][nodes]"), is(false));
@ -56,36 +56,36 @@ public class PrivilegeTests extends ESTestCase {
public void testCluster() throws Exception {
Privilege.Name name = new Privilege.Name("monitor");
Privilege.Cluster cluster = Privilege.Cluster.get(name);
assertThat(cluster, is(Privilege.Cluster.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");
cluster = Privilege.Cluster.get(name);
assertThat(cluster, is(Privilege.Cluster.ALL));
cluster = ClusterPrivilege.get(name);
assertThat(cluster, is(ClusterPrivilege.ALL));
name = new Privilege.Name("monitor", "none");
cluster = Privilege.Cluster.get(name);
assertThat(cluster, is(Privilege.Cluster.MONITOR));
cluster = ClusterPrivilege.get(name);
assertThat(cluster, is(ClusterPrivilege.MONITOR));
Privilege.Name name2 = new Privilege.Name("none", "monitor");
Privilege.Cluster cluster2 = Privilege.Cluster.get(name2);
ClusterPrivilege cluster2 = ClusterPrivilege.get(name2);
assertThat(cluster, is(cluster2));
}
public void testClusterTemplateActions() throws Exception {
Privilege.Name name = new Privilege.Name("indices:admin/template/delete");
Privilege.Cluster cluster = Privilege.Cluster.get(name);
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");
cluster = Privilege.Cluster.get(name);
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");
cluster = Privilege.Cluster.get(name);
cluster = ClusterPrivilege.get(name);
assertThat(cluster, notNullValue());
assertThat(cluster.predicate().test("indices:admin/template/put"), is(true));
}
@ -93,28 +93,28 @@ public class PrivilegeTests extends ESTestCase {
public void testClusterInvalidName() throws Exception {
thrown.expect(IllegalArgumentException.class);
Privilege.Name actionName = new Privilege.Name("foobar");
Privilege.Cluster.get(actionName);
ClusterPrivilege.get(actionName);
}
public void testClusterAction() throws Exception {
Privilege.Name actionName = new Privilege.Name("cluster:admin/snapshot/delete");
Privilege.Cluster cluster = Privilege.Cluster.get(actionName);
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 {
Privilege.Cluster.addCustom("foo", "cluster:bar");
ClusterPrivilege.addCustom("foo", "cluster:bar");
boolean found = false;
for (Privilege.Cluster cluster : Privilege.Cluster.values()) {
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));
Privilege.Cluster cluster = Privilege.Cluster.get(new Privilege.Name("foo"));
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));
@ -122,7 +122,7 @@ public class PrivilegeTests extends ESTestCase {
public void testClusterAddCustomInvalidPattern() throws Exception {
try {
Privilege.Cluster.addCustom("foo", "bar");
ClusterPrivilege.addCustom("foo", "bar");
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("cannot register custom cluster privilege [foo]"));
@ -132,7 +132,7 @@ public class PrivilegeTests extends ESTestCase {
public void testClusterAddCustomAlreadyExists() throws Exception {
try {
Privilege.Cluster.addCustom("all", "bar");
ClusterPrivilege.addCustom("all", "bar");
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("cannot register custom cluster privilege [all]"));
@ -142,19 +142,19 @@ public class PrivilegeTests extends ESTestCase {
public void testIndexAction() throws Exception {
Privilege.Name actionName = new Privilege.Name("indices:admin/mapping/delete");
Privilege.Index index = Privilege.Index.get(actionName);
IndexPrivilege index = IndexPrivilege.get(actionName);
assertThat(index, notNullValue());
assertThat(index.predicate().test("indices:admin/mapping/delete"), is(true));
assertThat(index.predicate().test("indices:admin/mapping/dele"), is(false));
}
public void testIndexCollapse() throws Exception {
Privilege.Index[] values = Privilege.Index.values().toArray(new Privilege.Index[Privilege.Index.values().size()]);
Privilege.Index first = values[randomIntBetween(0, values.length-1)];
Privilege.Index second = values[randomIntBetween(0, values.length-1)];
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());
Privilege.Index index = Privilege.Index.get(name);
IndexPrivilege index = IndexPrivilege.get(name);
if (first.implies(second)) {
assertThat(index, is(first));
@ -166,12 +166,12 @@ public class PrivilegeTests extends ESTestCase {
}
public void testIndexImplies() throws Exception {
Privilege.Index[] values = Privilege.Index.values().toArray(new Privilege.Index[Privilege.Index.values().size()]);
Privilege.Index first = values[randomIntBetween(0, values.length-1)];
Privilege.Index second = values[randomIntBetween(0, values.length-1)];
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());
Privilege.Index index = Privilege.Index.get(name);
IndexPrivilege index = IndexPrivilege.get(name);
assertThat(index.implies(first), is(true));
assertThat(index.implies(second), is(true));
@ -182,13 +182,13 @@ public class PrivilegeTests extends ESTestCase {
if (second.implies(first)) {
if (index != second) {
Privilege.Index idx = Privilege.Index.get(name);
IndexPrivilege idx = IndexPrivilege.get(name);
idx.name().toString();
}
assertThat(index, is(second));
}
for (Privilege.Index other : Privilege.Index.values()) {
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)) {
@ -198,16 +198,16 @@ public class PrivilegeTests extends ESTestCase {
}
public void testIndexAddCustom() throws Exception {
Privilege.Index.addCustom("foo", "indices:bar");
IndexPrivilege.addCustom("foo", "indices:bar");
boolean found = false;
for (Privilege.Index index : Privilege.Index.values()) {
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));
Privilege.Index index = Privilege.Index.get(new Privilege.Name("foo"));
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));
@ -215,7 +215,7 @@ public class PrivilegeTests extends ESTestCase {
public void testIndexAddCustomInvalidPattern() throws Exception {
try {
Privilege.Index.addCustom("foo", "bar");
IndexPrivilege.addCustom("foo", "bar");
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("cannot register custom index privilege [foo]"));
@ -225,7 +225,7 @@ public class PrivilegeTests extends ESTestCase {
public void testIndexAddCustomAlreadyExists() throws Exception {
try {
Privilege.Index.addCustom("all", "bar");
IndexPrivilege.addCustom("all", "bar");
fail("Expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertThat(e.getMessage(), containsString("cannot register custom index privilege [all]"));
@ -234,7 +234,7 @@ public class PrivilegeTests extends ESTestCase {
}
public void testSystem() throws Exception {
Predicate<String> predicate = Privilege.SYSTEM.predicate();
Predicate<String> predicate = SystemPrivilege.INSTANCE.predicate();
assertThat(predicate.test("indices:monitor/whatever"), is(true));
assertThat(predicate.test("cluster:monitor/whatever"), is(true));
assertThat(predicate.test("cluster:admin/snapshot/status[nodes]"), is(false));
@ -250,7 +250,7 @@ public class PrivilegeTests extends ESTestCase {
}
public void testSearchPrivilege() throws Exception {
Predicate<String> predicate = Privilege.Index.SEARCH.predicate();
Predicate<String> predicate = IndexPrivilege.SEARCH.predicate();
assertThat(predicate.test(SearchAction.NAME), is(true));
assertThat(predicate.test(SearchAction.NAME + "/whatever"), is(true));
assertThat(predicate.test(MultiSearchAction.NAME), is(true));
@ -265,7 +265,7 @@ public class PrivilegeTests extends ESTestCase {
}
public void testGetPrivilege() throws Exception {
Predicate<String> predicate = Privilege.Index.GET.predicate();
Predicate<String> predicate = IndexPrivilege.GET.predicate();
assertThat(predicate.test(GetAction.NAME), is(true));
assertThat(predicate.test(GetAction.NAME + "/whatever"), is(true));
assertThat(predicate.test(MultiGetAction.NAME), is(true));

View File

@ -10,8 +10,12 @@ import org.elasticsearch.env.Environment;
import org.elasticsearch.shield.ShieldPlugin;
import org.elasticsearch.shield.audit.logfile.CapturingLogger;
import org.elasticsearch.shield.authc.support.RefreshListener;
import org.elasticsearch.shield.authz.Permission;
import org.elasticsearch.shield.authz.Privilege;
import org.elasticsearch.shield.authz.permission.ClusterPermission;
import org.elasticsearch.shield.authz.permission.IndicesPermission;
import org.elasticsearch.shield.authz.permission.Role;
import org.elasticsearch.shield.authz.permission.RunAsPermission;
import org.elasticsearch.shield.authz.privilege.ClusterPrivilege;
import org.elasticsearch.shield.authz.privilege.IndexPrivilege;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.watcher.ResourceWatcherService;
@ -48,86 +52,86 @@ import static org.hamcrest.Matchers.startsWith;
public class FileRolesStoreTests extends ESTestCase {
public void testParseFile() throws Exception {
Path path = getDataPath("roles.yml");
Map<String, Permission.Global.Role> roles = FileRolesStore.parseFile(path, Collections.<Permission.Global.Role>emptySet(),
Map<String, Role> roles = FileRolesStore.parseFile(path, Collections.<Role>emptySet(),
logger, Settings.builder().put(ShieldPlugin.DLS_FLS_ENABLED_SETTING, true).build());
assertThat(roles, notNullValue());
assertThat(roles.size(), is(10));
Permission.Global.Role role = roles.get("role1");
Role role = roles.get("role1");
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role1"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster().privilege(), is(Privilege.Cluster.ALL));
assertThat(role.cluster().privilege(), is(ClusterPrivilege.ALL));
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
assertThat(role.indices().groups().length, is(2));
assertThat(role.runAs(), is(Permission.RunAs.Core.NONE));
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
Permission.Global.Indices.Group group = role.indices().groups()[0];
IndicesPermission.Group group = role.indices().groups()[0];
assertThat(group.indices(), notNullValue());
assertThat(group.indices().length, is(2));
assertThat(group.indices()[0], equalTo("idx1"));
assertThat(group.indices()[1], equalTo("idx2"));
assertThat(group.privilege(), notNullValue());
assertThat(group.privilege(), is(Privilege.Index.READ));
assertThat(group.privilege(), is(IndexPrivilege.READ));
group = role.indices().groups()[1];
assertThat(group.indices(), notNullValue());
assertThat(group.indices().length, is(1));
assertThat(group.indices()[0], equalTo("idx3"));
assertThat(group.privilege(), notNullValue());
assertThat(group.privilege(), is(Privilege.Index.CRUD));
assertThat(group.privilege(), is(IndexPrivilege.CRUD));
role = roles.get("role1.ab");
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role1.ab"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster().privilege(), is(Privilege.Cluster.ALL));
assertThat(role.cluster().privilege(), is(ClusterPrivilege.ALL));
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
assertThat(role.indices().groups().length, is(0));
assertThat(role.runAs(), is(Permission.RunAs.Core.NONE));
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
role = roles.get("role2");
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role2"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster().privilege(), is(Privilege.Cluster.ALL)); // MONITOR is collapsed into ALL
assertThat(role.cluster().privilege(), is(ClusterPrivilege.ALL)); // MONITOR is collapsed into ALL
assertThat(role.indices(), notNullValue());
assertThat(role.indices(), is(Permission.Indices.Core.NONE));
assertThat(role.runAs(), is(Permission.RunAs.Core.NONE));
assertThat(role.indices(), is(IndicesPermission.Core.NONE));
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
role = roles.get("role3");
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role3"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(Permission.Cluster.Core.NONE));
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
assertThat(role.indices().groups().length, is(1));
assertThat(role.runAs(), is(Permission.RunAs.Core.NONE));
assertThat(role.runAs(), is(RunAsPermission.Core.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(Privilege.Index.union(Privilege.Index.READ, Privilege.Index.WRITE)), is(true));
assertThat(group.privilege().isAlias(IndexPrivilege.union(IndexPrivilege.READ, IndexPrivilege.WRITE)), is(true));
role = roles.get("role4");
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role4"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(Permission.Cluster.Core.NONE));
assertThat(role.indices(), is(Permission.Indices.Core.NONE));
assertThat(role.runAs(), is(Permission.RunAs.Core.NONE));
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
assertThat(role.indices(), is(IndicesPermission.Core.NONE));
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
role = roles.get("role_run_as");
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role_run_as"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(Permission.Cluster.Core.NONE));
assertThat(role.indices(), is(Permission.Indices.Core.NONE));
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
assertThat(role.indices(), is(IndicesPermission.Core.NONE));
assertThat(role.runAs(), notNullValue());
assertThat(role.runAs().check("user1"), is(true));
assertThat(role.runAs().check("user2"), is(true));
@ -137,8 +141,8 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role_run_as1"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(Permission.Cluster.Core.NONE));
assertThat(role.indices(), is(Permission.Indices.Core.NONE));
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
assertThat(role.indices(), is(IndicesPermission.Core.NONE));
assertThat(role.runAs(), notNullValue());
assertThat(role.runAs().check("user1"), is(true));
assertThat(role.runAs().check("user2"), is(true));
@ -148,8 +152,8 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role_fields"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(Permission.Cluster.Core.NONE));
assertThat(role.runAs(), is(Permission.RunAs.Core.NONE));
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
assertThat(role.indices().groups().length, is(1));
@ -159,15 +163,15 @@ 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(Privilege.Index.READ), is(true));
assertThat(group.privilege().isAlias(IndexPrivilege.READ), is(true));
assertThat(group.getFields(), contains("foo", "boo"));
role = roles.get("role_query");
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role_query"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(Permission.Cluster.Core.NONE));
assertThat(role.runAs(), is(Permission.RunAs.Core.NONE));
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
assertThat(role.indices().groups().length, is(1));
@ -177,7 +181,7 @@ 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(Privilege.Index.READ), is(true));
assertThat(group.privilege().isAlias(IndexPrivilege.READ), is(true));
assertThat(group.getFields(), nullValue());
assertThat(group.getQuery(), notNullValue());
@ -185,8 +189,8 @@ public class FileRolesStoreTests extends ESTestCase {
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("role_query_fields"));
assertThat(role.cluster(), notNullValue());
assertThat(role.cluster(), is(Permission.Cluster.Core.NONE));
assertThat(role.runAs(), is(Permission.RunAs.Core.NONE));
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
assertThat(role.indices(), notNullValue());
assertThat(role.indices().groups(), notNullValue());
assertThat(role.indices().groups().length, is(1));
@ -196,7 +200,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(Privilege.Index.READ), is(true));
assertThat(group.privilege().isAlias(IndexPrivilege.READ), is(true));
assertThat(group.getFields(), contains("foo", "boo"));
assertThat(group.getQuery(), notNullValue());
}
@ -204,7 +208,7 @@ public class FileRolesStoreTests extends ESTestCase {
public void testParseFileWithFLSAndDLSDisabled() throws Exception {
Path path = getDataPath("roles.yml");
CapturingLogger logger = new CapturingLogger(CapturingLogger.Level.ERROR);
Map<String, Permission.Global.Role> roles = FileRolesStore.parseFile(path, Collections.<Permission.Global.Role>emptySet(),
Map<String, Role> roles = FileRolesStore.parseFile(path, Collections.<Role>emptySet(),
logger, Settings.builder().put(ShieldPlugin.DLS_FLS_ENABLED_SETTING, false).build());
assertThat(roles, notNullValue());
assertThat(roles.size(), is(7));
@ -224,7 +228,7 @@ public class FileRolesStoreTests extends ESTestCase {
*/
public void testDefaultRolesFile() throws Exception {
Path path = getDataPath("default_roles.yml");
Map<String, Permission.Global.Role> roles = FileRolesStore.parseFile(path, Collections.<Permission.Global.Role>emptySet(), logger, Settings.EMPTY);
Map<String, Role> roles = FileRolesStore.parseFile(path, Collections.<Role>emptySet(), logger, Settings.EMPTY);
assertThat(roles, notNullValue());
assertThat(roles.size(), is(8));
@ -258,7 +262,7 @@ public class FileRolesStoreTests extends ESTestCase {
threadPool = new ThreadPool("test");
watcherService = new ResourceWatcherService(settings, threadPool);
final CountDownLatch latch = new CountDownLatch(1);
FileRolesStore store = new FileRolesStore(settings, env, watcherService, Collections.<Permission.Global.Role>emptySet(), new RefreshListener() {
FileRolesStore store = new FileRolesStore(settings, env, watcherService, Collections.<Role>emptySet(), new RefreshListener() {
@Override
public void onRefresh() {
latch.countDown();
@ -266,7 +270,7 @@ public class FileRolesStoreTests extends ESTestCase {
});
store.start();
Permission.Global.Role role = store.role("role1");
Role role = store.role("role1");
assertThat(role, notNullValue());
role = store.role("role5");
assertThat(role, nullValue());
@ -302,17 +306,17 @@ public class FileRolesStoreTests extends ESTestCase {
public void testThatEmptyFileDoesNotResultInLoop() throws Exception {
Path file = createTempFile();
Files.write(file, Collections.singletonList("#"), StandardCharsets.UTF_8);
Map<String, Permission.Global.Role> roles = FileRolesStore.parseFile(file, Collections.<Permission.Global.Role>emptySet(), logger, Settings.EMPTY);
Map<String, Role> roles = FileRolesStore.parseFile(file, Collections.<Role>emptySet(), logger, Settings.EMPTY);
assertThat(roles.keySet(), is(empty()));
}
public void testThatInvalidRoleDefinitions() throws Exception {
Path path = getDataPath("invalid_roles.yml");
CapturingLogger logger = new CapturingLogger(CapturingLogger.Level.ERROR);
Map<String, Permission.Global.Role> roles = FileRolesStore.parseFile(path, Collections.<Permission.Global.Role>emptySet(), logger, Settings.EMPTY);
Map<String, Role> roles = FileRolesStore.parseFile(path, Collections.<Role>emptySet(), logger, Settings.EMPTY);
assertThat(roles.size(), is(1));
assertThat(roles, hasKey("valid_role"));
Permission.Global.Role role = roles.get("valid_role");
Role role = roles.get("valid_role");
assertThat(role, notNullValue());
assertThat(role.name(), equalTo("valid_role"));
@ -338,20 +342,20 @@ public class FileRolesStoreTests extends ESTestCase {
}
public void testReservedRoles() throws Exception {
Set<Permission.Global.Role> reservedRoles = singleton(Permission.Global.Role.builder("reserved")
.cluster(Privilege.Cluster.ALL)
Set<Role> reservedRoles = singleton(Role.builder("reserved")
.cluster(ClusterPrivilege.ALL)
.build());
CapturingLogger logger = new CapturingLogger(CapturingLogger.Level.INFO);
Path path = getDataPath("reserved_roles.yml");
Map<String, Permission.Global.Role> roles = FileRolesStore.parseFile(path, reservedRoles, logger, Settings.EMPTY);
Map<String, Role> roles = FileRolesStore.parseFile(path, reservedRoles, logger, Settings.EMPTY);
assertThat(roles, notNullValue());
assertThat(roles.size(), is(2));
assertThat(roles, hasKey("admin"));
assertThat(roles, hasKey("reserved"));
Permission.Global.Role reserved = roles.get("reserved");
Role reserved = roles.get("reserved");
List<CapturingLogger.Msg> messages = logger.output(CapturingLogger.Level.WARN);
assertThat(messages, notNullValue());
@ -368,8 +372,8 @@ public class FileRolesStoreTests extends ESTestCase {
}
public void testReservedRolesNonExistentRolesFile() throws Exception {
Set<Permission.Global.Role> reservedRoles = singleton(Permission.Global.Role.builder("reserved")
.cluster(Privilege.Cluster.ALL)
Set<Role> reservedRoles = singleton(Role.builder("reserved")
.cluster(ClusterPrivilege.ALL)
.build());
CapturingLogger logger = new CapturingLogger(CapturingLogger.Level.INFO);
@ -377,12 +381,12 @@ public class FileRolesStoreTests extends ESTestCase {
Path path = createTempFile();
Files.delete(path);
assertThat(Files.exists(path), is(false));
Map<String, Permission.Global.Role> roles = FileRolesStore.parseFile(path, reservedRoles, logger, Settings.EMPTY);
Map<String, Role> roles = FileRolesStore.parseFile(path, reservedRoles, logger, Settings.EMPTY);
assertThat(roles, notNullValue());
assertThat(roles.size(), is(1));
assertThat(roles, hasKey("reserved"));
Permission.Global.Role reserved = roles.get("reserved");
Role reserved = roles.get("reserved");
List<CapturingLogger.Msg> messages = logger.output(CapturingLogger.Level.WARN);
assertThat(messages, notNullValue());

View File

@ -6,8 +6,9 @@
package org.elasticsearch.watcher.shield;
import org.elasticsearch.shield.User;
import org.elasticsearch.shield.authz.Permission;
import org.elasticsearch.shield.authz.Privilege;
import org.elasticsearch.shield.authz.permission.Role;
import org.elasticsearch.shield.authz.privilege.ClusterPrivilege;
import org.elasticsearch.shield.authz.privilege.IndexPrivilege;
/**
*
@ -19,8 +20,8 @@ public class InternalWatcherUser extends User {
public static final InternalWatcherUser INSTANCE = new InternalWatcherUser(NAME, ROLE_NAMES);
public static final Permission.Global.Role ROLE = Permission.Global.Role.builder(ROLE_NAMES[0])
.cluster(Privilege.Cluster.action("indices:admin/template/put"))
public static final Role ROLE = Role.builder(ROLE_NAMES[0])
.cluster(ClusterPrivilege.action("indices:admin/template/put"))
// for now, the watches will be executed under the watcher user, meaning, all actions
// taken as part of the execution will be executed on behalf of this user. this includes
@ -29,7 +30,7 @@ public class InternalWatcherUser extends User {
//
// at later phases we'll want to execute the watch on behalf of the user who registers
// it. this will require some work to attache/persist that user to/with the watch.
.add(Privilege.Index.ALL, "*")
.add(IndexPrivilege.ALL, "*")
.build();
InternalWatcherUser(String username, String[] roles) {

View File

@ -9,7 +9,7 @@ import org.elasticsearch.common.inject.AbstractModule;
import org.elasticsearch.common.logging.ESLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.shield.authz.Privilege;
import org.elasticsearch.shield.authz.privilege.ClusterPrivilege;
/**
*
@ -31,7 +31,7 @@ public class WatcherShieldModule extends AbstractModule {
void registerClusterPrivilege(String name, String... patterns) {
try {
Privilege.Cluster.addCustom(name, patterns);
ClusterPrivilege.addCustom(name, patterns);
} catch (Exception se) {
logger.warn("could not register cluster privilege [{}]", name);