cleanup shield's `Privilege` and `Permission` constructs
- broke down these classes to multiple top level classes - also `Role` is not a top level class Original commit: elastic/x-pack-elasticsearch@8900f869e1
This commit is contained in:
parent
1a7c03c13d
commit
978996e088
|
@ -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.action.admin.indices.template.put.PutIndexTemplateAction;
|
||||||
import org.elasticsearch.marvel.agent.settings.MarvelSettings;
|
import org.elasticsearch.marvel.agent.settings.MarvelSettings;
|
||||||
import org.elasticsearch.shield.User;
|
import org.elasticsearch.shield.User;
|
||||||
import org.elasticsearch.shield.authz.Permission;
|
import org.elasticsearch.shield.authz.permission.Role;
|
||||||
import org.elasticsearch.shield.authz.Privilege;
|
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 InternalMarvelUser INSTANCE = new InternalMarvelUser(NAME, ROLE_NAMES);
|
||||||
|
|
||||||
public static final Permission.Global.Role ROLE = Permission.Global.Role.builder(ROLE_NAMES[0])
|
public static final Role ROLE = Role.builder(ROLE_NAMES[0])
|
||||||
.cluster(Privilege.Cluster.get(new Privilege.Name(
|
.cluster(ClusterPrivilege.get(new Privilege.Name(
|
||||||
PutIndexTemplateAction.NAME + "*",
|
PutIndexTemplateAction.NAME + "*",
|
||||||
GetIndexTemplatesAction.NAME + "*",
|
GetIndexTemplatesAction.NAME + "*",
|
||||||
Privilege.Cluster.MONITOR.name().toString())))
|
ClusterPrivilege.MONITOR.name().toString())))
|
||||||
|
|
||||||
// we need all monitoring access
|
// we need all monitoring access
|
||||||
.add(Privilege.Index.MONITOR, "*")
|
.add(IndexPrivilege.MONITOR, "*")
|
||||||
|
|
||||||
// and full access to .marvel-es-* and .marvel-es-data indices
|
// 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
|
// note, we don't need _license permission as we're taking the licenses
|
||||||
// directly form the license service.
|
// directly form the license service.
|
||||||
|
|
|
@ -23,7 +23,7 @@ import org.elasticsearch.shield.action.interceptor.RequestInterceptor;
|
||||||
import org.elasticsearch.shield.audit.AuditTrail;
|
import org.elasticsearch.shield.audit.AuditTrail;
|
||||||
import org.elasticsearch.shield.authc.AuthenticationService;
|
import org.elasticsearch.shield.authc.AuthenticationService;
|
||||||
import org.elasticsearch.shield.authz.AuthorizationService;
|
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.crypto.CryptoService;
|
||||||
import org.elasticsearch.shield.license.ShieldLicenseState;
|
import org.elasticsearch.shield.license.ShieldLicenseState;
|
||||||
import org.elasticsearch.tasks.Task;
|
import org.elasticsearch.tasks.Task;
|
||||||
|
@ -41,7 +41,7 @@ import static org.elasticsearch.shield.support.Exceptions.authorizationError;
|
||||||
*/
|
*/
|
||||||
public class ShieldActionFilter extends AbstractComponent implements ActionFilter {
|
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 AuthenticationService authcService;
|
||||||
private final AuthorizationService authzService;
|
private final AuthorizationService authzService;
|
||||||
|
|
|
@ -7,8 +7,10 @@ package org.elasticsearch.shield.admin;
|
||||||
|
|
||||||
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
|
import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction;
|
||||||
import org.elasticsearch.shield.User;
|
import org.elasticsearch.shield.User;
|
||||||
import org.elasticsearch.shield.authz.Permission;
|
import org.elasticsearch.shield.authz.permission.Role;
|
||||||
import org.elasticsearch.shield.authz.Privilege;
|
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}
|
* 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 NAME = "__es_internal_user";
|
||||||
private static final String[] ROLES = new String[] { "__es_internal_role" };
|
private static final String[] ROLES = new String[] { "__es_internal_role" };
|
||||||
public static final Permission.Global.Role ROLE = Permission.Global.Role.builder(ROLES[0])
|
public static final Role ROLE = 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*")))
|
.cluster(ClusterPrivilege.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)
|
.add(IndexPrivilege.ALL, ShieldTemplateService.SHIELD_ADMIN_INDEX_NAME)
|
||||||
.build();
|
.build();
|
||||||
private static final User SHIELD_INTERNAL_USER = new User(NAME, ROLES);
|
private static final User SHIELD_INTERNAL_USER = new User(NAME, ROLES);
|
||||||
|
|
||||||
|
|
|
@ -43,18 +43,18 @@ import org.elasticsearch.common.xcontent.XContentFactory;
|
||||||
import org.elasticsearch.gateway.GatewayService;
|
import org.elasticsearch.gateway.GatewayService;
|
||||||
import org.elasticsearch.rest.RestRequest;
|
import org.elasticsearch.rest.RestRequest;
|
||||||
import org.elasticsearch.shield.admin.ShieldInternalUserHolder;
|
import org.elasticsearch.shield.admin.ShieldInternalUserHolder;
|
||||||
|
import org.elasticsearch.shield.authz.privilege.SystemPrivilege;
|
||||||
import org.elasticsearch.xpack.XPackPlugin;
|
import org.elasticsearch.xpack.XPackPlugin;
|
||||||
import org.elasticsearch.shield.User;
|
import org.elasticsearch.shield.User;
|
||||||
import org.elasticsearch.shield.audit.AuditTrail;
|
import org.elasticsearch.shield.audit.AuditTrail;
|
||||||
import org.elasticsearch.shield.authc.AuthenticationService;
|
import org.elasticsearch.shield.authc.AuthenticationService;
|
||||||
import org.elasticsearch.shield.authc.AuthenticationToken;
|
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.rest.RemoteHostHeader;
|
||||||
import org.elasticsearch.shield.transport.filter.ShieldIpFilterRule;
|
import org.elasticsearch.shield.transport.filter.ShieldIpFilterRule;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
import org.elasticsearch.transport.Transport;
|
import org.elasticsearch.transport.Transport;
|
||||||
import org.elasticsearch.transport.TransportMessage;
|
import org.elasticsearch.transport.TransportMessage;
|
||||||
import org.elasticsearch.xpack.XPackPlugin;
|
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.joda.time.DateTimeZone;
|
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) {
|
public void accessGranted(User user, String action, TransportMessage<?> message) {
|
||||||
if (!principalIsAuditor(user.principal())) {
|
if (!principalIsAuditor(user.principal())) {
|
||||||
// special treatment for internal system actions - only log if explicitly told to
|
// 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)) {
|
if (events.contains(SYSTEM_ACCESS_GRANTED)) {
|
||||||
try {
|
try {
|
||||||
enqueue(message("access_granted", action, user, indices(message), message), "access_granted");
|
enqueue(message("access_granted", action, user, indices(message), message), "access_granted");
|
||||||
|
|
|
@ -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.admin.indices.template.put.PutIndexTemplateAction;
|
||||||
import org.elasticsearch.action.bulk.BulkAction;
|
import org.elasticsearch.action.bulk.BulkAction;
|
||||||
import org.elasticsearch.shield.User;
|
import org.elasticsearch.shield.User;
|
||||||
import org.elasticsearch.shield.authz.Permission;
|
import org.elasticsearch.shield.authz.permission.Role;
|
||||||
import org.elasticsearch.shield.authz.Privilege;
|
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 NAME = "__indexing_audit_user";
|
||||||
private static final String[] ROLE_NAMES = new String[] { "__indexing_audit_role" };
|
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])
|
public static final Role ROLE = Role.builder(ROLE_NAMES[0])
|
||||||
.cluster(Privilege.Cluster.action(PutIndexTemplateAction.NAME))
|
.cluster(ClusterPrivilege.action(PutIndexTemplateAction.NAME))
|
||||||
.add(Privilege.Index.CREATE_INDEX, IndexAuditTrail.INDEX_NAME_PREFIX + "*")
|
.add(IndexPrivilege.CREATE_INDEX, IndexAuditTrail.INDEX_NAME_PREFIX + "*")
|
||||||
.add(Privilege.Index.INDEX, IndexAuditTrail.INDEX_NAME_PREFIX + "*")
|
.add(IndexPrivilege.INDEX, IndexAuditTrail.INDEX_NAME_PREFIX + "*")
|
||||||
.add(Privilege.Index.action(IndicesExistsAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*")
|
.add(IndexPrivilege.action(IndicesExistsAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*")
|
||||||
.add(Privilege.Index.action(BulkAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*")
|
.add(IndexPrivilege.action(BulkAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*")
|
||||||
.add(Privilege.Index.action(PutMappingAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*")
|
.add(IndexPrivilege.action(PutMappingAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
private final User user;
|
private final User user;
|
||||||
|
|
|
@ -20,7 +20,8 @@ import org.elasticsearch.shield.User;
|
||||||
import org.elasticsearch.shield.admin.ShieldInternalUserHolder;
|
import org.elasticsearch.shield.admin.ShieldInternalUserHolder;
|
||||||
import org.elasticsearch.shield.audit.AuditTrail;
|
import org.elasticsearch.shield.audit.AuditTrail;
|
||||||
import org.elasticsearch.shield.authc.AuthenticationToken;
|
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.rest.RemoteHostHeader;
|
||||||
import org.elasticsearch.shield.transport.filter.ShieldIpFilterRule;
|
import org.elasticsearch.shield.transport.filter.ShieldIpFilterRule;
|
||||||
import org.elasticsearch.transport.Transport;
|
import org.elasticsearch.transport.Transport;
|
||||||
|
@ -195,7 +196,7 @@ public class LoggingAuditTrail extends AbstractLifecycleComponent<LoggingAuditTr
|
||||||
String indices = indicesString(message);
|
String indices = indicesString(message);
|
||||||
|
|
||||||
// special treatment for internal system actions - only log on trace
|
// 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 (logger.isTraceEnabled()) {
|
||||||
if (indices != null) {
|
if (indices != null) {
|
||||||
logger.trace("{}[transport] [access_granted]\t{}, {}, action=[{}], indices=[{}], request=[{}]", prefix, originAttributes(message, transport), principal(user), action, indices, message.getClass().getSimpleName());
|
logger.trace("{}[transport] [access_granted]\t{}, {}, action=[{}], indices=[{}], request=[{}]", prefix, originAttributes(message, transport), principal(user), action, indices, message.getClass().getSimpleName());
|
||||||
|
|
|
@ -9,6 +9,7 @@ import org.elasticsearch.common.inject.multibindings.Multibinder;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.shield.authc.esnative.ESNativeUsersStore;
|
import org.elasticsearch.shield.authc.esnative.ESNativeUsersStore;
|
||||||
import org.elasticsearch.shield.authz.esnative.ESNativeRolesStore;
|
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.CompositeRolesStore;
|
||||||
import org.elasticsearch.shield.authz.store.FileRolesStore;
|
import org.elasticsearch.shield.authz.store.FileRolesStore;
|
||||||
import org.elasticsearch.shield.authz.store.RolesStore;
|
import org.elasticsearch.shield.authz.store.RolesStore;
|
||||||
|
@ -22,21 +23,21 @@ import java.util.Set;
|
||||||
*/
|
*/
|
||||||
public class AuthorizationModule extends AbstractShieldModule.Node {
|
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) {
|
public AuthorizationModule(Settings settings) {
|
||||||
super(settings);
|
super(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerReservedRole(Permission.Global.Role role) {
|
public void registerReservedRole(Role role) {
|
||||||
reservedRoles.add(role);
|
reservedRoles.add(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void configureNode() {
|
protected void configureNode() {
|
||||||
|
|
||||||
Multibinder<Permission.Global.Role> reservedRolesBinder = Multibinder.newSetBinder(binder(), Permission.Global.Role.class);
|
Multibinder<Role> reservedRolesBinder = Multibinder.newSetBinder(binder(), Role.class);
|
||||||
for (Permission.Global.Role reservedRole : reservedRoles) {
|
for (Role reservedRole : reservedRoles) {
|
||||||
reservedRolesBinder.addBinding().toInstance(reservedRole);
|
reservedRolesBinder.addBinding().toInstance(reservedRole);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,12 @@ import org.elasticsearch.shield.authc.AuthenticationFailureHandler;
|
||||||
import org.elasticsearch.shield.authz.accesscontrol.IndicesAccessControl;
|
import org.elasticsearch.shield.authz.accesscontrol.IndicesAccessControl;
|
||||||
import org.elasticsearch.shield.authz.indicesresolver.DefaultIndicesAndAliasesResolver;
|
import org.elasticsearch.shield.authz.indicesresolver.DefaultIndicesAndAliasesResolver;
|
||||||
import org.elasticsearch.shield.authz.indicesresolver.IndicesAndAliasesResolver;
|
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.shield.authz.store.RolesStore;
|
||||||
import org.elasticsearch.transport.TransportRequest;
|
import org.elasticsearch.transport.TransportRequest;
|
||||||
|
|
||||||
|
@ -70,15 +76,15 @@ public class InternalAuthorizationService extends AbstractComponent implements A
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> authorizedIndicesAndAliases(User user, String action) {
|
public List<String> authorizedIndicesAndAliases(User user, String action) {
|
||||||
String[] roles = user.roles();
|
String[] rolesNames = user.roles();
|
||||||
if (roles.length == 0) {
|
if (rolesNames.length == 0) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
List<Predicate<String>> predicates = new ArrayList<>();
|
List<Predicate<String>> predicates = new ArrayList<>();
|
||||||
for (String role : roles) {
|
for (String roleName : rolesNames) {
|
||||||
Permission.Global.Role global = rolesStore.role(role);
|
Role role = rolesStore.role(roleName);
|
||||||
if (global != null) {
|
if (role != null) {
|
||||||
predicates.add(global.indices().allowedIndicesMatcher(action));
|
predicates.add(role.indices().allowedIndicesMatcher(action));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +113,7 @@ public class InternalAuthorizationService extends AbstractComponent implements A
|
||||||
throw denial(user, action, request);
|
throw denial(user, action, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
Permission.Global permission = permission(user.roles());
|
GlobalPermission permission = permission(user.roles());
|
||||||
final boolean isRunAs = user.runAs() != null;
|
final boolean isRunAs = user.runAs() != null;
|
||||||
// permission can be null as it might be that the user's role
|
// permission can be null as it might be that the user's role
|
||||||
// is unknown
|
// is unknown
|
||||||
|
@ -123,7 +129,7 @@ public class InternalAuthorizationService extends AbstractComponent implements A
|
||||||
// check if the request is a run as request
|
// check if the request is a run as request
|
||||||
if (isRunAs) {
|
if (isRunAs) {
|
||||||
// first we must authorize for the RUN_AS action
|
// 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())) {
|
if (runAs != null && runAs.check(user.runAs().principal())) {
|
||||||
grantRunAs(user, action, request);
|
grantRunAs(user, action, request);
|
||||||
permission = permission(user.runAs().roles());
|
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
|
// first, we'll check if the action is a cluster action. If it is, we'll only check it
|
||||||
// against the cluster permissions
|
// against the cluster permissions
|
||||||
if (Privilege.Cluster.ACTION_MATCHER.test(action)) {
|
if (ClusterPrivilege.ACTION_MATCHER.test(action)) {
|
||||||
Permission.Cluster cluster = permission.cluster();
|
ClusterPermission cluster = permission.cluster();
|
||||||
if (cluster != null && cluster.check(action)) {
|
if (cluster != null && cluster.check(action)) {
|
||||||
request.putInContext(INDICES_PERMISSIONS_KEY, IndicesAccessControl.ALLOW_ALL);
|
request.putInContext(INDICES_PERMISSIONS_KEY, IndicesAccessControl.ALLOW_ALL);
|
||||||
grant(user, action, request);
|
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
|
// 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);
|
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 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;
|
assert request instanceof CreateIndexRequest;
|
||||||
Set<Alias> aliases = ((CreateIndexRequest) request).aliases();
|
Set<Alias> aliases = ((CreateIndexRequest) request).aliases();
|
||||||
if (!aliases.isEmpty()) {
|
if (!aliases.isEmpty()) {
|
||||||
|
@ -209,21 +215,21 @@ public class InternalAuthorizationService extends AbstractComponent implements A
|
||||||
grant(user, action, request);
|
grant(user, action, request);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Permission.Global permission(String[] roleNames) {
|
private GlobalPermission permission(String[] roleNames) {
|
||||||
if (roleNames.length == 0) {
|
if (roleNames.length == 0) {
|
||||||
return Permission.Global.NONE;
|
return GlobalPermission.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (roleNames.length == 1) {
|
if (roleNames.length == 1) {
|
||||||
Permission.Global.Role role = rolesStore.role(roleNames[0]);
|
Role role = rolesStore.role(roleNames[0]);
|
||||||
return role == null ? Permission.Global.NONE : role;
|
return role == null ? GlobalPermission.NONE : role;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we'll take all the roles and combine their associated permissions
|
// 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) {
|
for (String roleName : roleNames) {
|
||||||
Permission.Global role = rolesStore.role(roleName);
|
GlobalPermission role = rolesStore.role(roleName);
|
||||||
if (role != null) {
|
if (role != null) {
|
||||||
roles.add(role);
|
roles.add(role);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 & 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -5,6 +5,8 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.shield.authz;
|
package org.elasticsearch.shield.authz;
|
||||||
|
|
||||||
|
import org.elasticsearch.shield.authz.privilege.SystemPrivilege;
|
||||||
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -16,7 +18,7 @@ public class SystemRole {
|
||||||
|
|
||||||
public static final String NAME = "__es_system_role";
|
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() {
|
private SystemRole() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,6 @@ import org.elasticsearch.common.bytes.BytesReference;
|
||||||
import org.elasticsearch.common.component.AbstractComponent;
|
import org.elasticsearch.common.component.AbstractComponent;
|
||||||
import org.elasticsearch.common.inject.Inject;
|
import org.elasticsearch.common.inject.Inject;
|
||||||
import org.elasticsearch.common.inject.Provider;
|
import org.elasticsearch.common.inject.Provider;
|
||||||
import org.elasticsearch.common.logging.ESLogger;
|
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.unit.TimeValue;
|
import org.elasticsearch.common.unit.TimeValue;
|
||||||
import org.elasticsearch.common.util.concurrent.AbstractRunnable;
|
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.ShieldInternalUserHolder;
|
||||||
import org.elasticsearch.shield.admin.ShieldTemplateService;
|
import org.elasticsearch.shield.admin.ShieldTemplateService;
|
||||||
import org.elasticsearch.shield.authc.AuthenticationService;
|
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.RoleDescriptor;
|
||||||
import org.elasticsearch.shield.authz.store.RolesStore;
|
import org.elasticsearch.shield.authz.store.RolesStore;
|
||||||
import org.elasticsearch.shield.client.ShieldClient;
|
import org.elasticsearch.shield.client.ShieldClient;
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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*");
|
||||||
|
}
|
||||||
|
}
|
|
@ -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");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,9 +6,8 @@
|
||||||
package org.elasticsearch.shield.authz.store;
|
package org.elasticsearch.shield.authz.store;
|
||||||
|
|
||||||
import org.elasticsearch.common.inject.Inject;
|
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.esnative.ESNativeRolesStore;
|
||||||
|
import org.elasticsearch.shield.authz.permission.Role;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A composite roles store that combines file-based and index-based roles
|
* A composite roles store that combines file-based and index-based roles
|
||||||
|
@ -25,9 +24,9 @@ public class CompositeRolesStore implements RolesStore {
|
||||||
this.nativeRolesStore = nativeRolesStore;
|
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
|
// 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) {
|
if (fileRole != null) {
|
||||||
return fileRole;
|
return fileRole;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,9 +22,11 @@ import org.elasticsearch.common.xcontent.yaml.YamlXContent;
|
||||||
import org.elasticsearch.env.Environment;
|
import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.shield.ShieldPlugin;
|
import org.elasticsearch.shield.ShieldPlugin;
|
||||||
import org.elasticsearch.shield.authc.support.RefreshListener;
|
import org.elasticsearch.shield.authc.support.RefreshListener;
|
||||||
import org.elasticsearch.shield.authz.Permission;
|
import org.elasticsearch.shield.authz.permission.Role;
|
||||||
import org.elasticsearch.shield.authz.Privilege;
|
import org.elasticsearch.shield.authz.privilege.ClusterPrivilege;
|
||||||
import org.elasticsearch.shield.authz.RoleDescriptor;
|
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.authz.SystemRole;
|
||||||
import org.elasticsearch.shield.support.NoOpLogger;
|
import org.elasticsearch.shield.support.NoOpLogger;
|
||||||
import org.elasticsearch.shield.support.Validation;
|
import org.elasticsearch.shield.support.Validation;
|
||||||
|
@ -62,17 +64,17 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
|
||||||
|
|
||||||
private final Path file;
|
private final Path file;
|
||||||
private final RefreshListener listener;
|
private final RefreshListener listener;
|
||||||
private final Set<Permission.Global.Role> reservedRoles;
|
private final Set<Role> reservedRoles;
|
||||||
private final ResourceWatcherService watcherService;
|
private final ResourceWatcherService watcherService;
|
||||||
|
|
||||||
private volatile Map<String, Permission.Global.Role> permissions;
|
private volatile Map<String, Role> permissions;
|
||||||
|
|
||||||
@Inject
|
@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);
|
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);
|
super(settings);
|
||||||
this.file = resolveFile(settings, env);
|
this.file = resolveFile(settings, env);
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
|
@ -102,7 +104,7 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Permission.Global.Role role(String role) {
|
public Role role(String role) {
|
||||||
return permissions.get(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) {
|
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) {
|
if (roleMap == null) {
|
||||||
return emptySet();
|
return emptySet();
|
||||||
}
|
}
|
||||||
return roleMap.keySet();
|
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);
|
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) {
|
if (logger == null) {
|
||||||
logger = NoOpLogger.INSTANCE;
|
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());
|
logger.trace("attempted to read roles file located at [{}]", path.toAbsolutePath());
|
||||||
if (Files.exists(path)) {
|
if (Files.exists(path)) {
|
||||||
try {
|
try {
|
||||||
List<String> roleSegments = roleSegments(path);
|
List<String> roleSegments = roleSegments(path);
|
||||||
for (String segment : roleSegments) {
|
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 (role != null) {
|
||||||
if (SystemRole.NAME.equals(role.name())) {
|
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);
|
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)
|
// 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())) {
|
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());
|
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);
|
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;
|
String roleName = null;
|
||||||
try {
|
try {
|
||||||
XContentParser parser = YamlXContent.yamlXContent.createParser(segment);
|
XContentParser parser = YamlXContent.yamlXContent.createParser(segment);
|
||||||
|
@ -179,9 +181,9 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Permission.Global.Role.Builder permission = Permission.Global.Role.builder(roleName);
|
Role.Builder role = Role.builder(roleName);
|
||||||
if (resolvePermissions == false) {
|
if (resolvePermissions == false) {
|
||||||
return permission.build();
|
return role.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
token = parser.nextToken();
|
token = parser.nextToken();
|
||||||
|
@ -216,7 +218,7 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
|
||||||
}
|
}
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
try {
|
try {
|
||||||
permission.cluster(Privilege.Cluster.get(name));
|
role.cluster(ClusterPrivilege.get(name));
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
logger.error("invalid role definition [{}] in roles file [{}]. could not resolve cluster privileges [{}]. skipping role...", roleName, path.toAbsolutePath(), name);
|
logger.error("invalid role definition [{}] in roles file [{}]. could not resolve cluster privileges [{}]. skipping role...", roleName, path.toAbsolutePath(), name);
|
||||||
return null;
|
return null;
|
||||||
|
@ -307,7 +309,7 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
permission.add(fields, query, Privilege.Index.get(name), indices);
|
role.add(fields, query, IndexPrivilege.get(name), indices);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
logger.error("invalid role definition [{}] in roles file [{}]. could not resolve indices privileges [{}]. skipping role...", roleName, path.toAbsolutePath(), name);
|
logger.error("invalid role definition [{}] in roles file [{}]. could not resolve indices privileges [{}]. skipping role...", roleName, path.toAbsolutePath(), name);
|
||||||
return null;
|
return null;
|
||||||
|
@ -321,7 +323,7 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
|
||||||
}
|
}
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
try {
|
try {
|
||||||
permission.add(Privilege.Index.get(name), indices);
|
role.add(IndexPrivilege.get(name), indices);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
logger.error("invalid role definition [{}] in roles file [{}]. could not resolve indices privileges [{}]. skipping role...", roleName, path.toAbsolutePath(), name);
|
logger.error("invalid role definition [{}] in roles file [{}]. could not resolve indices privileges [{}]. skipping role...", roleName, path.toAbsolutePath(), name);
|
||||||
return null;
|
return null;
|
||||||
|
@ -358,7 +360,7 @@ public class FileRolesStore extends AbstractLifecycleComponent<RolesStore> imple
|
||||||
if (!names.isEmpty()) {
|
if (!names.isEmpty()) {
|
||||||
Privilege.Name name = new Privilege.Name(names);
|
Privilege.Name name = new Privilege.Name(names);
|
||||||
try {
|
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) {
|
} catch (IllegalArgumentException e) {
|
||||||
logger.error("invalid role definition [{}] in roles file [{}]. could not resolve run_as privileges [{}]. skipping role...", roleName, path.toAbsolutePath(), name);
|
logger.error("invalid role definition [{}] in roles file [{}]. could not resolve run_as privileges [{}]. skipping role...", roleName, path.toAbsolutePath(), name);
|
||||||
return null;
|
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());
|
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());
|
logger.error("invalid role definition [{}] in roles file [{}]. skipping role...", roleName, path.toAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,14 +5,13 @@
|
||||||
*/
|
*/
|
||||||
package org.elasticsearch.shield.authz.store;
|
package org.elasticsearch.shield.authz.store;
|
||||||
|
|
||||||
import org.elasticsearch.shield.authz.Permission;
|
import org.elasticsearch.shield.authz.permission.Role;
|
||||||
import org.elasticsearch.shield.authz.RoleDescriptor;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface for looking up a role given a string role name
|
* An interface for looking up a role given a string role name
|
||||||
*/
|
*/
|
||||||
public interface RolesStore {
|
public interface RolesStore {
|
||||||
|
|
||||||
Permission.Global.Role role(String role);
|
Role role(String role);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,9 @@ import org.elasticsearch.shield.action.admin.user.DeleteUserResponse;
|
||||||
import org.elasticsearch.shield.action.admin.user.GetUsersResponse;
|
import org.elasticsearch.shield.action.admin.user.GetUsersResponse;
|
||||||
import org.elasticsearch.shield.authc.esnative.ESNativeUsersStore;
|
import org.elasticsearch.shield.authc.esnative.ESNativeUsersStore;
|
||||||
import org.elasticsearch.shield.authc.support.SecuredString;
|
import org.elasticsearch.shield.authc.support.SecuredString;
|
||||||
import org.elasticsearch.shield.authz.Permission;
|
|
||||||
import org.elasticsearch.shield.authz.RoleDescriptor;
|
import org.elasticsearch.shield.authz.RoleDescriptor;
|
||||||
import org.elasticsearch.shield.authz.esnative.ESNativeRolesStore;
|
import org.elasticsearch.shield.authz.esnative.ESNativeRolesStore;
|
||||||
|
import org.elasticsearch.shield.authz.permission.Role;
|
||||||
import org.elasticsearch.shield.client.ShieldClient;
|
import org.elasticsearch.shield.client.ShieldClient;
|
||||||
import org.elasticsearch.test.ShieldIntegTestCase;
|
import org.elasticsearch.test.ShieldIntegTestCase;
|
||||||
import org.elasticsearch.test.ShieldSettingsSource;
|
import org.elasticsearch.test.ShieldSettingsSource;
|
||||||
|
@ -322,7 +322,7 @@ public class ESNativeTests extends ShieldIntegTestCase {
|
||||||
GetRolesResponse getRolesResponse = c.prepareGetRoles().roles("test_role").get();
|
GetRolesResponse getRolesResponse = c.prepareGetRoles().roles("test_role").get();
|
||||||
assertTrue("test_role does not exist!", getRolesResponse.isExists());
|
assertTrue("test_role does not exist!", getRolesResponse.isExists());
|
||||||
assertTrue("any cluster permission should be authorized",
|
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()
|
c.prepareAddRole()
|
||||||
.name("test_role")
|
.name("test_role")
|
||||||
.cluster("none")
|
.cluster("none")
|
||||||
|
@ -333,7 +333,7 @@ public class ESNativeTests extends ShieldIntegTestCase {
|
||||||
assertTrue("test_role does not exist!", getRolesResponse.isExists());
|
assertTrue("test_role does not exist!", getRolesResponse.isExists());
|
||||||
|
|
||||||
assertFalse("no cluster permission should be authorized",
|
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"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,10 @@ import org.elasticsearch.shield.User;
|
||||||
import org.elasticsearch.shield.audit.AuditTrail;
|
import org.elasticsearch.shield.audit.AuditTrail;
|
||||||
import org.elasticsearch.shield.authc.AnonymousService;
|
import org.elasticsearch.shield.authc.AnonymousService;
|
||||||
import org.elasticsearch.shield.authc.DefaultAuthenticationFailureHandler;
|
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.shield.authz.store.RolesStore;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
import org.elasticsearch.transport.TransportRequest;
|
import org.elasticsearch.transport.TransportRequest;
|
||||||
|
@ -139,7 +143,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
|
||||||
public void testThatNonIndicesAndNonClusterActionIsDenied() {
|
public void testThatNonIndicesAndNonClusterActionIsDenied() {
|
||||||
TransportRequest request = mock(TransportRequest.class);
|
TransportRequest request = mock(TransportRequest.class);
|
||||||
User user = new User("test user", "a_all");
|
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 {
|
try {
|
||||||
internalAuthorizationService.authorize(user, "whatever", request);
|
internalAuthorizationService.authorize(user, "whatever", request);
|
||||||
|
@ -154,7 +158,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
|
||||||
public void testThatRoleWithNoIndicesIsDenied() {
|
public void testThatRoleWithNoIndicesIsDenied() {
|
||||||
TransportRequest request = new IndicesExistsRequest("a");
|
TransportRequest request = new IndicesExistsRequest("a");
|
||||||
User user = new User("test user", "no_indices");
|
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 {
|
try {
|
||||||
internalAuthorizationService.authorize(user, "indices:a", request);
|
internalAuthorizationService.authorize(user, "indices:a", request);
|
||||||
|
@ -168,7 +172,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
|
||||||
|
|
||||||
public void testScrollRelatedRequestsAllowed() {
|
public void testScrollRelatedRequestsAllowed() {
|
||||||
User user = new User("test user", "a_all");
|
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();
|
ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
|
||||||
internalAuthorizationService.authorize(user, ClearScrollAction.NAME, clearScrollRequest);
|
internalAuthorizationService.authorize(user, ClearScrollAction.NAME, clearScrollRequest);
|
||||||
|
@ -201,7 +205,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
|
||||||
TransportRequest request = new IndicesExistsRequest("b");
|
TransportRequest request = new IndicesExistsRequest("b");
|
||||||
ClusterState state = mock(ClusterState.class);
|
ClusterState state = mock(ClusterState.class);
|
||||||
User user = new User("test user", "a_all");
|
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(clusterService.state()).thenReturn(state);
|
||||||
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
|
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
|
||||||
|
|
||||||
|
@ -222,7 +226,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
|
||||||
request.alias(new Alias("a2"));
|
request.alias(new Alias("a2"));
|
||||||
ClusterState state = mock(ClusterState.class);
|
ClusterState state = mock(ClusterState.class);
|
||||||
User user = new User("test user", "a_all");
|
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(clusterService.state()).thenReturn(state);
|
||||||
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
|
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
|
||||||
|
|
||||||
|
@ -243,7 +247,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
|
||||||
request.alias(new Alias("a2"));
|
request.alias(new Alias("a2"));
|
||||||
ClusterState state = mock(ClusterState.class);
|
ClusterState state = mock(ClusterState.class);
|
||||||
User user = new User("test user", "a_all");
|
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(clusterService.state()).thenReturn(state);
|
||||||
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
|
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
|
||||||
|
|
||||||
|
@ -265,8 +269,8 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
|
||||||
public void testIndicesAliasesWithUserHavingRoles() {
|
public void testIndicesAliasesWithUserHavingRoles() {
|
||||||
User user = new User("test user", "a_star", "b");
|
User user = new User("test user", "a_star", "b");
|
||||||
ClusterState state = mock(ClusterState.class);
|
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("a_star")).thenReturn(Role.builder("a_star").add(IndexPrivilege.ALL, "a*").build());
|
||||||
when(rolesStore.role("b")).thenReturn(Permission.Global.Role.builder("a_star").add(Privilege.Index.SEARCH, "b").build());
|
when(rolesStore.role("b")).thenReturn(Role.builder("a_star").add(IndexPrivilege.SEARCH, "b").build());
|
||||||
when(clusterService.state()).thenReturn(state);
|
when(clusterService.state()).thenReturn(state);
|
||||||
Settings indexSettings = Settings.builder().put("index.version.created", Version.CURRENT).build();
|
Settings indexSettings = Settings.builder().put("index.version.created", Version.CURRENT).build();
|
||||||
when(state.metaData()).thenReturn(MetaData.builder()
|
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());
|
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());
|
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(clusterService.state()).thenReturn(state);
|
||||||
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
|
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
|
||||||
|
|
||||||
|
@ -320,7 +324,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
|
||||||
.build());
|
.build());
|
||||||
internalAuthorizationService = new InternalAuthorizationService(Settings.EMPTY, rolesStore, clusterService, auditTrail, anonymousService, new DefaultAuthenticationFailureHandler());
|
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(clusterService.state()).thenReturn(state);
|
||||||
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
|
when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA);
|
||||||
|
|
||||||
|
@ -354,10 +358,10 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
|
||||||
TransportRequest request = mock(TransportRequest.class);
|
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" }));
|
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()));
|
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")
|
.builder("can run as")
|
||||||
.runAs(new Privilege.General("", "not the right user"))
|
.runAs(new GeneralPrivilege("", "not the right user"))
|
||||||
.add(Privilege.Index.ALL, "a")
|
.add(IndexPrivilege.ALL, "a")
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -374,10 +378,10 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
|
||||||
TransportRequest request = new IndicesExistsRequest("a");
|
TransportRequest request = new IndicesExistsRequest("a");
|
||||||
User user = new User("test user", new String[] { "can run as" }, new User("run as me", new String[] { "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()));
|
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")
|
.builder("can run as")
|
||||||
.runAs(new Privilege.General("", "run as me"))
|
.runAs(new GeneralPrivilege("", "run as me"))
|
||||||
.add(Privilege.Index.ALL, "a")
|
.add(IndexPrivilege.ALL, "a")
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
if (randomBoolean()) {
|
if (randomBoolean()) {
|
||||||
|
@ -388,9 +392,9 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
|
||||||
.settings(Settings.builder().put("index.version.created", Version.CURRENT).build())
|
.settings(Settings.builder().put("index.version.created", Version.CURRENT).build())
|
||||||
.numberOfShards(1).numberOfReplicas(0).build(), true)
|
.numberOfShards(1).numberOfReplicas(0).build(), true)
|
||||||
.build());
|
.build());
|
||||||
when(rolesStore.role("b")).thenReturn(Permission.Global.Role
|
when(rolesStore.role("b")).thenReturn(Role
|
||||||
.builder("b")
|
.builder("b")
|
||||||
.add(Privilege.Index.ALL, "b")
|
.add(IndexPrivilege.ALL, "b")
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,10 +413,10 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
|
||||||
TransportRequest request = new IndicesExistsRequest("b");
|
TransportRequest request = new IndicesExistsRequest("b");
|
||||||
User user = new User("test user", new String[] { "can run as" }, new User("run as me", new String[] { "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()));
|
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")
|
.builder("can run as")
|
||||||
.runAs(new Privilege.General("", "run as me"))
|
.runAs(new GeneralPrivilege("", "run as me"))
|
||||||
.add(Privilege.Index.ALL, "a")
|
.add(IndexPrivilege.ALL, "a")
|
||||||
.build());
|
.build());
|
||||||
ClusterState state = mock(ClusterState.class);
|
ClusterState state = mock(ClusterState.class);
|
||||||
when(clusterService.state()).thenReturn(state);
|
when(clusterService.state()).thenReturn(state);
|
||||||
|
@ -421,9 +425,9 @@ public class InternalAuthorizationServiceTests extends ESTestCase {
|
||||||
.settings(Settings.builder().put("index.version.created", Version.CURRENT).build())
|
.settings(Settings.builder().put("index.version.created", Version.CURRENT).build())
|
||||||
.numberOfShards(1).numberOfReplicas(0).build(), true)
|
.numberOfShards(1).numberOfReplicas(0).build(), true)
|
||||||
.build());
|
.build());
|
||||||
when(rolesStore.role("b")).thenReturn(Permission.Global.Role
|
when(rolesStore.role("b")).thenReturn(Role
|
||||||
.builder("b")
|
.builder("b")
|
||||||
.add(Privilege.Index.ALL, "b")
|
.add(IndexPrivilege.ALL, "b")
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
internalAuthorizationService.authorize(user, "indices:a", request);
|
internalAuthorizationService.authorize(user, "indices:a", request);
|
||||||
|
|
|
@ -14,8 +14,8 @@ import org.elasticsearch.common.bytes.BytesArray;
|
||||||
import org.elasticsearch.common.bytes.BytesReference;
|
import org.elasticsearch.common.bytes.BytesReference;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
import org.elasticsearch.common.settings.Settings;
|
||||||
import org.elasticsearch.common.util.set.Sets;
|
import org.elasticsearch.common.util.set.Sets;
|
||||||
import org.elasticsearch.shield.authz.Permission;
|
import org.elasticsearch.shield.authz.permission.Role;
|
||||||
import org.elasticsearch.shield.authz.Privilege;
|
import org.elasticsearch.shield.authz.privilege.IndexPrivilege;
|
||||||
import org.elasticsearch.test.ESTestCase;
|
import org.elasticsearch.test.ESTestCase;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
@ -40,7 +40,7 @@ public class IndicesPermissionTests extends ESTestCase {
|
||||||
// basics:
|
// basics:
|
||||||
BytesReference query = new BytesArray("{}");
|
BytesReference query = new BytesArray("{}");
|
||||||
List<String> fields = Arrays.asList("_field");
|
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);
|
IndicesAccessControl permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md);
|
||||||
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
|
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
|
||||||
assertThat(permissions.getIndexPermissions("_index").getFields().size(), equalTo(1));
|
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));
|
assertThat(permissions.getIndexPermissions("_index").getQueries().iterator().next(), equalTo(query));
|
||||||
|
|
||||||
// no document level security:
|
// 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);
|
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md);
|
||||||
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
|
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
|
||||||
assertThat(permissions.getIndexPermissions("_index").getFields().size(), equalTo(1));
|
assertThat(permissions.getIndexPermissions("_index").getFields().size(), equalTo(1));
|
||||||
|
@ -57,7 +57,7 @@ public class IndicesPermissionTests extends ESTestCase {
|
||||||
assertThat(permissions.getIndexPermissions("_index").getQueries(), nullValue());
|
assertThat(permissions.getIndexPermissions("_index").getQueries(), nullValue());
|
||||||
|
|
||||||
// no field level security:
|
// 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);
|
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md);
|
||||||
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
|
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
|
||||||
assertThat(permissions.getIndexPermissions("_index").getFields(), nullValue());
|
assertThat(permissions.getIndexPermissions("_index").getFields(), nullValue());
|
||||||
|
@ -65,7 +65,7 @@ public class IndicesPermissionTests extends ESTestCase {
|
||||||
assertThat(permissions.getIndexPermissions("_index").getQueries().iterator().next(), equalTo(query));
|
assertThat(permissions.getIndexPermissions("_index").getQueries().iterator().next(), equalTo(query));
|
||||||
|
|
||||||
// index group associated with an alias:
|
// 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);
|
permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_alias"), md);
|
||||||
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
|
assertThat(permissions.getIndexPermissions("_index"), notNullValue());
|
||||||
assertThat(permissions.getIndexPermissions("_index").getFields().size(), equalTo(1));
|
assertThat(permissions.getIndexPermissions("_index").getFields().size(), equalTo(1));
|
||||||
|
|
|
@ -3,10 +3,11 @@
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
* you may not use this file except in compliance with 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.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.elasticsearch.test.ESTestCase;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
||||||
|
@ -15,10 +16,10 @@ import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import static org.elasticsearch.shield.authz.Privilege.Index.MONITOR;
|
import static org.elasticsearch.shield.authz.privilege.IndexPrivilege.MONITOR;
|
||||||
import static org.elasticsearch.shield.authz.Privilege.Index.READ;
|
import static org.elasticsearch.shield.authz.privilege.IndexPrivilege.READ;
|
||||||
import static org.elasticsearch.shield.authz.Privilege.Index.SEARCH;
|
import static org.elasticsearch.shield.authz.privilege.IndexPrivilege.SEARCH;
|
||||||
import static org.elasticsearch.shield.authz.Privilege.Index.union;
|
import static org.elasticsearch.shield.authz.privilege.IndexPrivilege.union;
|
||||||
import static org.hamcrest.Matchers.equalTo;
|
import static org.hamcrest.Matchers.equalTo;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.hamcrest.Matchers.notNullValue;
|
import static org.hamcrest.Matchers.notNullValue;
|
||||||
|
@ -27,11 +28,11 @@ import static org.hamcrest.Matchers.notNullValue;
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class PermissionTests extends ESTestCase {
|
public class PermissionTests extends ESTestCase {
|
||||||
private Permission.Global.Role permission;
|
private Role permission;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void init() {
|
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(SEARCH, MONITOR), "test_*", "/foo.*/");
|
||||||
builder.add(union(READ), "baz_*foo", "/fool.*bar/");
|
builder.add(union(READ), "baz_*foo", "/fool.*bar/");
|
||||||
builder.add(union(MONITOR), "/bar.*/");
|
builder.add(union(MONITOR), "/bar.*/");
|
||||||
|
@ -49,12 +50,12 @@ public class PermissionTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIndicesGlobalsIterator() {
|
public void testIndicesGlobalsIterator() {
|
||||||
Permission.Global.Role.Builder builder = Permission.Global.Role.builder("tc_role");
|
Role.Builder builder = Role.builder("tc_role");
|
||||||
builder.cluster(Cluster.action("cluster:monitor/nodes/info"));
|
builder.cluster(ClusterPrivilege.action("cluster:monitor/nodes/info"));
|
||||||
Permission.Global.Role noIndicesPermission = builder.build();
|
Role noIndicesPermission = builder.build();
|
||||||
|
|
||||||
Permission.Indices.Globals indicesGlobals = new Permission.Indices.Globals(Collections.<Permission.Global>unmodifiableList(Arrays.asList(noIndicesPermission, permission)));
|
IndicesPermission.Globals indicesGlobals = new IndicesPermission.Globals(Collections.<GlobalPermission>unmodifiableList(Arrays.asList(noIndicesPermission, permission)));
|
||||||
Iterator<Permission.Indices.Group> iterator = indicesGlobals.iterator();
|
Iterator<IndicesPermission.Group> iterator = indicesGlobals.iterator();
|
||||||
assertThat(iterator.hasNext(), is(equalTo(true)));
|
assertThat(iterator.hasNext(), is(equalTo(true)));
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
|
@ -65,8 +66,8 @@ public class PermissionTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testBuildEmptyRole() {
|
public void testBuildEmptyRole() {
|
||||||
Permission.Global.Role.Builder permission = Permission.Global.Role.builder("some_role");
|
Role.Builder permission = Role.builder("some_role");
|
||||||
Permission.Global.Role role = permission.build();
|
Role role = permission.build();
|
||||||
assertThat(role, notNullValue());
|
assertThat(role, notNullValue());
|
||||||
assertThat(role.cluster(), notNullValue());
|
assertThat(role.cluster(), notNullValue());
|
||||||
assertThat(role.indices(), notNullValue());
|
assertThat(role.indices(), notNullValue());
|
||||||
|
@ -74,8 +75,8 @@ public class PermissionTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testRunAs() {
|
public void testRunAs() {
|
||||||
Permission.Global.Role permission = Permission.Global.Role.builder("some_role")
|
Role permission = Role.builder("some_role")
|
||||||
.runAs(new Privilege.General("name", "user1", "run*"))
|
.runAs(new GeneralPrivilege("name", "user1", "run*"))
|
||||||
.build();
|
.build();
|
||||||
assertThat(permission.runAs().check("user1"), is(true));
|
assertThat(permission.runAs().check("user1"), is(true));
|
||||||
assertThat(permission.runAs().check("user"), is(false));
|
assertThat(permission.runAs().check("user"), is(false));
|
|
@ -3,7 +3,7 @@
|
||||||
* or more contributor license agreements. Licensed under the Elastic License;
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
* you may not use this file except in compliance with 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.GetAction;
|
||||||
import org.elasticsearch.action.get.MultiGetAction;
|
import org.elasticsearch.action.get.MultiGetAction;
|
||||||
|
@ -47,7 +47,7 @@ public class PrivilegeTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSubActionPattern() throws Exception {
|
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][nodes]"), is(true));
|
||||||
assertThat(predicate.test("foo[n]"), is(true));
|
assertThat(predicate.test("foo[n]"), is(true));
|
||||||
assertThat(predicate.test("bar[n][nodes]"), is(false));
|
assertThat(predicate.test("bar[n][nodes]"), is(false));
|
||||||
|
@ -56,36 +56,36 @@ public class PrivilegeTests extends ESTestCase {
|
||||||
|
|
||||||
public void testCluster() throws Exception {
|
public void testCluster() throws Exception {
|
||||||
Privilege.Name name = new Privilege.Name("monitor");
|
Privilege.Name name = new Privilege.Name("monitor");
|
||||||
Privilege.Cluster cluster = Privilege.Cluster.get(name);
|
ClusterPrivilege cluster = ClusterPrivilege.get(name);
|
||||||
assertThat(cluster, is(Privilege.Cluster.MONITOR));
|
assertThat(cluster, is(ClusterPrivilege.MONITOR));
|
||||||
|
|
||||||
// since "all" implies "monitor", this should collapse to All
|
// since "all" implies "monitor", this should collapse to All
|
||||||
name = new Privilege.Name("monitor", "all");
|
name = new Privilege.Name("monitor", "all");
|
||||||
cluster = Privilege.Cluster.get(name);
|
cluster = ClusterPrivilege.get(name);
|
||||||
assertThat(cluster, is(Privilege.Cluster.ALL));
|
assertThat(cluster, is(ClusterPrivilege.ALL));
|
||||||
|
|
||||||
name = new Privilege.Name("monitor", "none");
|
name = new Privilege.Name("monitor", "none");
|
||||||
cluster = Privilege.Cluster.get(name);
|
cluster = ClusterPrivilege.get(name);
|
||||||
assertThat(cluster, is(Privilege.Cluster.MONITOR));
|
assertThat(cluster, is(ClusterPrivilege.MONITOR));
|
||||||
|
|
||||||
Privilege.Name name2 = new Privilege.Name("none", "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));
|
assertThat(cluster, is(cluster2));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testClusterTemplateActions() throws Exception {
|
public void testClusterTemplateActions() throws Exception {
|
||||||
Privilege.Name name = new Privilege.Name("indices:admin/template/delete");
|
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, notNullValue());
|
||||||
assertThat(cluster.predicate().test("indices:admin/template/delete"), is(true));
|
assertThat(cluster.predicate().test("indices:admin/template/delete"), is(true));
|
||||||
|
|
||||||
name = new Privilege.Name("indices:admin/template/get");
|
name = new Privilege.Name("indices:admin/template/get");
|
||||||
cluster = Privilege.Cluster.get(name);
|
cluster = ClusterPrivilege.get(name);
|
||||||
assertThat(cluster, notNullValue());
|
assertThat(cluster, notNullValue());
|
||||||
assertThat(cluster.predicate().test("indices:admin/template/get"), is(true));
|
assertThat(cluster.predicate().test("indices:admin/template/get"), is(true));
|
||||||
|
|
||||||
name = new Privilege.Name("indices:admin/template/put");
|
name = new Privilege.Name("indices:admin/template/put");
|
||||||
cluster = Privilege.Cluster.get(name);
|
cluster = ClusterPrivilege.get(name);
|
||||||
assertThat(cluster, notNullValue());
|
assertThat(cluster, notNullValue());
|
||||||
assertThat(cluster.predicate().test("indices:admin/template/put"), is(true));
|
assertThat(cluster.predicate().test("indices:admin/template/put"), is(true));
|
||||||
}
|
}
|
||||||
|
@ -93,28 +93,28 @@ public class PrivilegeTests extends ESTestCase {
|
||||||
public void testClusterInvalidName() throws Exception {
|
public void testClusterInvalidName() throws Exception {
|
||||||
thrown.expect(IllegalArgumentException.class);
|
thrown.expect(IllegalArgumentException.class);
|
||||||
Privilege.Name actionName = new Privilege.Name("foobar");
|
Privilege.Name actionName = new Privilege.Name("foobar");
|
||||||
Privilege.Cluster.get(actionName);
|
ClusterPrivilege.get(actionName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testClusterAction() throws Exception {
|
public void testClusterAction() throws Exception {
|
||||||
Privilege.Name actionName = new Privilege.Name("cluster:admin/snapshot/delete");
|
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, notNullValue());
|
||||||
assertThat(cluster.predicate().test("cluster:admin/snapshot/delete"), is(true));
|
assertThat(cluster.predicate().test("cluster:admin/snapshot/delete"), is(true));
|
||||||
assertThat(cluster.predicate().test("cluster:admin/snapshot/dele"), is(false));
|
assertThat(cluster.predicate().test("cluster:admin/snapshot/dele"), is(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testClusterAddCustom() throws Exception {
|
public void testClusterAddCustom() throws Exception {
|
||||||
Privilege.Cluster.addCustom("foo", "cluster:bar");
|
ClusterPrivilege.addCustom("foo", "cluster:bar");
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (Privilege.Cluster cluster : Privilege.Cluster.values()) {
|
for (ClusterPrivilege cluster : ClusterPrivilege.values()) {
|
||||||
if ("foo".equals(cluster.name.toString())) {
|
if ("foo".equals(cluster.name.toString())) {
|
||||||
found = true;
|
found = true;
|
||||||
assertThat(cluster.predicate().test("cluster:bar"), is(true));
|
assertThat(cluster.predicate().test("cluster:bar"), is(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertThat(found, 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, notNullValue());
|
||||||
assertThat(cluster.name().toString(), is("foo"));
|
assertThat(cluster.name().toString(), is("foo"));
|
||||||
assertThat(cluster.predicate().test("cluster:bar"), is(true));
|
assertThat(cluster.predicate().test("cluster:bar"), is(true));
|
||||||
|
@ -122,7 +122,7 @@ public class PrivilegeTests extends ESTestCase {
|
||||||
|
|
||||||
public void testClusterAddCustomInvalidPattern() throws Exception {
|
public void testClusterAddCustomInvalidPattern() throws Exception {
|
||||||
try {
|
try {
|
||||||
Privilege.Cluster.addCustom("foo", "bar");
|
ClusterPrivilege.addCustom("foo", "bar");
|
||||||
fail("Expected IllegalArgumentException");
|
fail("Expected IllegalArgumentException");
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
assertThat(e.getMessage(), containsString("cannot register custom cluster privilege [foo]"));
|
assertThat(e.getMessage(), containsString("cannot register custom cluster privilege [foo]"));
|
||||||
|
@ -132,7 +132,7 @@ public class PrivilegeTests extends ESTestCase {
|
||||||
|
|
||||||
public void testClusterAddCustomAlreadyExists() throws Exception {
|
public void testClusterAddCustomAlreadyExists() throws Exception {
|
||||||
try {
|
try {
|
||||||
Privilege.Cluster.addCustom("all", "bar");
|
ClusterPrivilege.addCustom("all", "bar");
|
||||||
fail("Expected IllegalArgumentException");
|
fail("Expected IllegalArgumentException");
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
assertThat(e.getMessage(), containsString("cannot register custom cluster privilege [all]"));
|
assertThat(e.getMessage(), containsString("cannot register custom cluster privilege [all]"));
|
||||||
|
@ -142,19 +142,19 @@ public class PrivilegeTests extends ESTestCase {
|
||||||
|
|
||||||
public void testIndexAction() throws Exception {
|
public void testIndexAction() throws Exception {
|
||||||
Privilege.Name actionName = new Privilege.Name("indices:admin/mapping/delete");
|
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, notNullValue());
|
||||||
assertThat(index.predicate().test("indices:admin/mapping/delete"), is(true));
|
assertThat(index.predicate().test("indices:admin/mapping/delete"), is(true));
|
||||||
assertThat(index.predicate().test("indices:admin/mapping/dele"), is(false));
|
assertThat(index.predicate().test("indices:admin/mapping/dele"), is(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIndexCollapse() throws Exception {
|
public void testIndexCollapse() throws Exception {
|
||||||
Privilege.Index[] values = Privilege.Index.values().toArray(new Privilege.Index[Privilege.Index.values().size()]);
|
IndexPrivilege[] values = IndexPrivilege.values().toArray(new IndexPrivilege[IndexPrivilege.values().size()]);
|
||||||
Privilege.Index first = values[randomIntBetween(0, values.length-1)];
|
IndexPrivilege first = values[randomIntBetween(0, values.length-1)];
|
||||||
Privilege.Index second = 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.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)) {
|
if (first.implies(second)) {
|
||||||
assertThat(index, is(first));
|
assertThat(index, is(first));
|
||||||
|
@ -166,12 +166,12 @@ public class PrivilegeTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIndexImplies() throws Exception {
|
public void testIndexImplies() throws Exception {
|
||||||
Privilege.Index[] values = Privilege.Index.values().toArray(new Privilege.Index[Privilege.Index.values().size()]);
|
IndexPrivilege[] values = IndexPrivilege.values().toArray(new IndexPrivilege[IndexPrivilege.values().size()]);
|
||||||
Privilege.Index first = values[randomIntBetween(0, values.length-1)];
|
IndexPrivilege first = values[randomIntBetween(0, values.length-1)];
|
||||||
Privilege.Index second = 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.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(first), is(true));
|
||||||
assertThat(index.implies(second), is(true));
|
assertThat(index.implies(second), is(true));
|
||||||
|
@ -182,13 +182,13 @@ public class PrivilegeTests extends ESTestCase {
|
||||||
|
|
||||||
if (second.implies(first)) {
|
if (second.implies(first)) {
|
||||||
if (index != second) {
|
if (index != second) {
|
||||||
Privilege.Index idx = Privilege.Index.get(name);
|
IndexPrivilege idx = IndexPrivilege.get(name);
|
||||||
idx.name().toString();
|
idx.name().toString();
|
||||||
}
|
}
|
||||||
assertThat(index, is(second));
|
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)) {
|
if (first.implies(other) || second.implies(other) || index.isAlias(other)) {
|
||||||
assertThat("index privilege [" + index + "] should imply [" + other + "]", index.implies(other), is(true));
|
assertThat("index privilege [" + index + "] should imply [" + other + "]", index.implies(other), is(true));
|
||||||
} else if (other.implies(first) && other.implies(second)) {
|
} else if (other.implies(first) && other.implies(second)) {
|
||||||
|
@ -198,16 +198,16 @@ public class PrivilegeTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testIndexAddCustom() throws Exception {
|
public void testIndexAddCustom() throws Exception {
|
||||||
Privilege.Index.addCustom("foo", "indices:bar");
|
IndexPrivilege.addCustom("foo", "indices:bar");
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
for (Privilege.Index index : Privilege.Index.values()) {
|
for (IndexPrivilege index : IndexPrivilege.values()) {
|
||||||
if ("foo".equals(index.name.toString())) {
|
if ("foo".equals(index.name.toString())) {
|
||||||
found = true;
|
found = true;
|
||||||
assertThat(index.predicate().test("indices:bar"), is(true));
|
assertThat(index.predicate().test("indices:bar"), is(true));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertThat(found, 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, notNullValue());
|
||||||
assertThat(index.name().toString(), is("foo"));
|
assertThat(index.name().toString(), is("foo"));
|
||||||
assertThat(index.predicate().test("indices:bar"), is(true));
|
assertThat(index.predicate().test("indices:bar"), is(true));
|
||||||
|
@ -215,7 +215,7 @@ public class PrivilegeTests extends ESTestCase {
|
||||||
|
|
||||||
public void testIndexAddCustomInvalidPattern() throws Exception {
|
public void testIndexAddCustomInvalidPattern() throws Exception {
|
||||||
try {
|
try {
|
||||||
Privilege.Index.addCustom("foo", "bar");
|
IndexPrivilege.addCustom("foo", "bar");
|
||||||
fail("Expected IllegalArgumentException");
|
fail("Expected IllegalArgumentException");
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
assertThat(e.getMessage(), containsString("cannot register custom index privilege [foo]"));
|
assertThat(e.getMessage(), containsString("cannot register custom index privilege [foo]"));
|
||||||
|
@ -225,7 +225,7 @@ public class PrivilegeTests extends ESTestCase {
|
||||||
|
|
||||||
public void testIndexAddCustomAlreadyExists() throws Exception {
|
public void testIndexAddCustomAlreadyExists() throws Exception {
|
||||||
try {
|
try {
|
||||||
Privilege.Index.addCustom("all", "bar");
|
IndexPrivilege.addCustom("all", "bar");
|
||||||
fail("Expected IllegalArgumentException");
|
fail("Expected IllegalArgumentException");
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
assertThat(e.getMessage(), containsString("cannot register custom index privilege [all]"));
|
assertThat(e.getMessage(), containsString("cannot register custom index privilege [all]"));
|
||||||
|
@ -234,7 +234,7 @@ public class PrivilegeTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSystem() throws Exception {
|
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("indices:monitor/whatever"), is(true));
|
||||||
assertThat(predicate.test("cluster:monitor/whatever"), is(true));
|
assertThat(predicate.test("cluster:monitor/whatever"), is(true));
|
||||||
assertThat(predicate.test("cluster:admin/snapshot/status[nodes]"), is(false));
|
assertThat(predicate.test("cluster:admin/snapshot/status[nodes]"), is(false));
|
||||||
|
@ -250,7 +250,7 @@ public class PrivilegeTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSearchPrivilege() throws Exception {
|
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), is(true));
|
||||||
assertThat(predicate.test(SearchAction.NAME + "/whatever"), is(true));
|
assertThat(predicate.test(SearchAction.NAME + "/whatever"), is(true));
|
||||||
assertThat(predicate.test(MultiSearchAction.NAME), is(true));
|
assertThat(predicate.test(MultiSearchAction.NAME), is(true));
|
||||||
|
@ -265,7 +265,7 @@ public class PrivilegeTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testGetPrivilege() throws Exception {
|
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), is(true));
|
||||||
assertThat(predicate.test(GetAction.NAME + "/whatever"), is(true));
|
assertThat(predicate.test(GetAction.NAME + "/whatever"), is(true));
|
||||||
assertThat(predicate.test(MultiGetAction.NAME), is(true));
|
assertThat(predicate.test(MultiGetAction.NAME), is(true));
|
|
@ -10,8 +10,12 @@ import org.elasticsearch.env.Environment;
|
||||||
import org.elasticsearch.shield.ShieldPlugin;
|
import org.elasticsearch.shield.ShieldPlugin;
|
||||||
import org.elasticsearch.shield.audit.logfile.CapturingLogger;
|
import org.elasticsearch.shield.audit.logfile.CapturingLogger;
|
||||||
import org.elasticsearch.shield.authc.support.RefreshListener;
|
import org.elasticsearch.shield.authc.support.RefreshListener;
|
||||||
import org.elasticsearch.shield.authz.Permission;
|
import org.elasticsearch.shield.authz.permission.ClusterPermission;
|
||||||
import org.elasticsearch.shield.authz.Privilege;
|
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.test.ESTestCase;
|
||||||
import org.elasticsearch.threadpool.ThreadPool;
|
import org.elasticsearch.threadpool.ThreadPool;
|
||||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||||
|
@ -48,86 +52,86 @@ import static org.hamcrest.Matchers.startsWith;
|
||||||
public class FileRolesStoreTests extends ESTestCase {
|
public class FileRolesStoreTests extends ESTestCase {
|
||||||
public void testParseFile() throws Exception {
|
public void testParseFile() throws Exception {
|
||||||
Path path = getDataPath("roles.yml");
|
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());
|
logger, Settings.builder().put(ShieldPlugin.DLS_FLS_ENABLED_SETTING, true).build());
|
||||||
assertThat(roles, notNullValue());
|
assertThat(roles, notNullValue());
|
||||||
assertThat(roles.size(), is(10));
|
assertThat(roles.size(), is(10));
|
||||||
|
|
||||||
Permission.Global.Role role = roles.get("role1");
|
Role role = roles.get("role1");
|
||||||
assertThat(role, notNullValue());
|
assertThat(role, notNullValue());
|
||||||
assertThat(role.name(), equalTo("role1"));
|
assertThat(role.name(), equalTo("role1"));
|
||||||
assertThat(role.cluster(), notNullValue());
|
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(), notNullValue());
|
||||||
assertThat(role.indices().groups(), notNullValue());
|
assertThat(role.indices().groups(), notNullValue());
|
||||||
assertThat(role.indices().groups().length, is(2));
|
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(), notNullValue());
|
||||||
assertThat(group.indices().length, is(2));
|
assertThat(group.indices().length, is(2));
|
||||||
assertThat(group.indices()[0], equalTo("idx1"));
|
assertThat(group.indices()[0], equalTo("idx1"));
|
||||||
assertThat(group.indices()[1], equalTo("idx2"));
|
assertThat(group.indices()[1], equalTo("idx2"));
|
||||||
assertThat(group.privilege(), notNullValue());
|
assertThat(group.privilege(), notNullValue());
|
||||||
assertThat(group.privilege(), is(Privilege.Index.READ));
|
assertThat(group.privilege(), is(IndexPrivilege.READ));
|
||||||
|
|
||||||
group = role.indices().groups()[1];
|
group = role.indices().groups()[1];
|
||||||
assertThat(group.indices(), notNullValue());
|
assertThat(group.indices(), notNullValue());
|
||||||
assertThat(group.indices().length, is(1));
|
assertThat(group.indices().length, is(1));
|
||||||
assertThat(group.indices()[0], equalTo("idx3"));
|
assertThat(group.indices()[0], equalTo("idx3"));
|
||||||
assertThat(group.privilege(), notNullValue());
|
assertThat(group.privilege(), notNullValue());
|
||||||
assertThat(group.privilege(), is(Privilege.Index.CRUD));
|
assertThat(group.privilege(), is(IndexPrivilege.CRUD));
|
||||||
|
|
||||||
role = roles.get("role1.ab");
|
role = roles.get("role1.ab");
|
||||||
assertThat(role, notNullValue());
|
assertThat(role, notNullValue());
|
||||||
assertThat(role.name(), equalTo("role1.ab"));
|
assertThat(role.name(), equalTo("role1.ab"));
|
||||||
assertThat(role.cluster(), notNullValue());
|
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(), notNullValue());
|
||||||
assertThat(role.indices().groups(), notNullValue());
|
assertThat(role.indices().groups(), notNullValue());
|
||||||
assertThat(role.indices().groups().length, is(0));
|
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");
|
role = roles.get("role2");
|
||||||
assertThat(role, notNullValue());
|
assertThat(role, notNullValue());
|
||||||
assertThat(role.name(), equalTo("role2"));
|
assertThat(role.name(), equalTo("role2"));
|
||||||
assertThat(role.cluster(), notNullValue());
|
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(), notNullValue());
|
||||||
assertThat(role.indices(), is(Permission.Indices.Core.NONE));
|
assertThat(role.indices(), is(IndicesPermission.Core.NONE));
|
||||||
assertThat(role.runAs(), is(Permission.RunAs.Core.NONE));
|
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
|
||||||
|
|
||||||
role = roles.get("role3");
|
role = roles.get("role3");
|
||||||
assertThat(role, notNullValue());
|
assertThat(role, notNullValue());
|
||||||
assertThat(role.name(), equalTo("role3"));
|
assertThat(role.name(), equalTo("role3"));
|
||||||
assertThat(role.cluster(), notNullValue());
|
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(), notNullValue());
|
||||||
assertThat(role.indices().groups(), notNullValue());
|
assertThat(role.indices().groups(), notNullValue());
|
||||||
assertThat(role.indices().groups().length, is(1));
|
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];
|
group = role.indices().groups()[0];
|
||||||
assertThat(group.indices(), notNullValue());
|
assertThat(group.indices(), notNullValue());
|
||||||
assertThat(group.indices().length, is(1));
|
assertThat(group.indices().length, is(1));
|
||||||
assertThat(group.indices()[0], equalTo("/.*_.*/"));
|
assertThat(group.indices()[0], equalTo("/.*_.*/"));
|
||||||
assertThat(group.privilege(), notNullValue());
|
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");
|
role = roles.get("role4");
|
||||||
assertThat(role, notNullValue());
|
assertThat(role, notNullValue());
|
||||||
assertThat(role.name(), equalTo("role4"));
|
assertThat(role.name(), equalTo("role4"));
|
||||||
assertThat(role.cluster(), notNullValue());
|
assertThat(role.cluster(), notNullValue());
|
||||||
assertThat(role.cluster(), is(Permission.Cluster.Core.NONE));
|
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
|
||||||
assertThat(role.indices(), is(Permission.Indices.Core.NONE));
|
assertThat(role.indices(), is(IndicesPermission.Core.NONE));
|
||||||
assertThat(role.runAs(), is(Permission.RunAs.Core.NONE));
|
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
|
||||||
|
|
||||||
role = roles.get("role_run_as");
|
role = roles.get("role_run_as");
|
||||||
assertThat(role, notNullValue());
|
assertThat(role, notNullValue());
|
||||||
assertThat(role.name(), equalTo("role_run_as"));
|
assertThat(role.name(), equalTo("role_run_as"));
|
||||||
assertThat(role.cluster(), notNullValue());
|
assertThat(role.cluster(), notNullValue());
|
||||||
assertThat(role.cluster(), is(Permission.Cluster.Core.NONE));
|
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
|
||||||
assertThat(role.indices(), is(Permission.Indices.Core.NONE));
|
assertThat(role.indices(), is(IndicesPermission.Core.NONE));
|
||||||
assertThat(role.runAs(), notNullValue());
|
assertThat(role.runAs(), notNullValue());
|
||||||
assertThat(role.runAs().check("user1"), is(true));
|
assertThat(role.runAs().check("user1"), is(true));
|
||||||
assertThat(role.runAs().check("user2"), is(true));
|
assertThat(role.runAs().check("user2"), is(true));
|
||||||
|
@ -137,8 +141,8 @@ public class FileRolesStoreTests extends ESTestCase {
|
||||||
assertThat(role, notNullValue());
|
assertThat(role, notNullValue());
|
||||||
assertThat(role.name(), equalTo("role_run_as1"));
|
assertThat(role.name(), equalTo("role_run_as1"));
|
||||||
assertThat(role.cluster(), notNullValue());
|
assertThat(role.cluster(), notNullValue());
|
||||||
assertThat(role.cluster(), is(Permission.Cluster.Core.NONE));
|
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
|
||||||
assertThat(role.indices(), is(Permission.Indices.Core.NONE));
|
assertThat(role.indices(), is(IndicesPermission.Core.NONE));
|
||||||
assertThat(role.runAs(), notNullValue());
|
assertThat(role.runAs(), notNullValue());
|
||||||
assertThat(role.runAs().check("user1"), is(true));
|
assertThat(role.runAs().check("user1"), is(true));
|
||||||
assertThat(role.runAs().check("user2"), is(true));
|
assertThat(role.runAs().check("user2"), is(true));
|
||||||
|
@ -148,8 +152,8 @@ public class FileRolesStoreTests extends ESTestCase {
|
||||||
assertThat(role, notNullValue());
|
assertThat(role, notNullValue());
|
||||||
assertThat(role.name(), equalTo("role_fields"));
|
assertThat(role.name(), equalTo("role_fields"));
|
||||||
assertThat(role.cluster(), notNullValue());
|
assertThat(role.cluster(), notNullValue());
|
||||||
assertThat(role.cluster(), is(Permission.Cluster.Core.NONE));
|
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
|
||||||
assertThat(role.runAs(), is(Permission.RunAs.Core.NONE));
|
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
|
||||||
assertThat(role.indices(), notNullValue());
|
assertThat(role.indices(), notNullValue());
|
||||||
assertThat(role.indices().groups(), notNullValue());
|
assertThat(role.indices().groups(), notNullValue());
|
||||||
assertThat(role.indices().groups().length, is(1));
|
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().length, is(1));
|
||||||
assertThat(group.indices()[0], equalTo("field_idx"));
|
assertThat(group.indices()[0], equalTo("field_idx"));
|
||||||
assertThat(group.privilege(), notNullValue());
|
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.getFields(), contains("foo", "boo"));
|
||||||
|
|
||||||
role = roles.get("role_query");
|
role = roles.get("role_query");
|
||||||
assertThat(role, notNullValue());
|
assertThat(role, notNullValue());
|
||||||
assertThat(role.name(), equalTo("role_query"));
|
assertThat(role.name(), equalTo("role_query"));
|
||||||
assertThat(role.cluster(), notNullValue());
|
assertThat(role.cluster(), notNullValue());
|
||||||
assertThat(role.cluster(), is(Permission.Cluster.Core.NONE));
|
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
|
||||||
assertThat(role.runAs(), is(Permission.RunAs.Core.NONE));
|
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
|
||||||
assertThat(role.indices(), notNullValue());
|
assertThat(role.indices(), notNullValue());
|
||||||
assertThat(role.indices().groups(), notNullValue());
|
assertThat(role.indices().groups(), notNullValue());
|
||||||
assertThat(role.indices().groups().length, is(1));
|
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().length, is(1));
|
||||||
assertThat(group.indices()[0], equalTo("query_idx"));
|
assertThat(group.indices()[0], equalTo("query_idx"));
|
||||||
assertThat(group.privilege(), notNullValue());
|
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.getFields(), nullValue());
|
||||||
assertThat(group.getQuery(), notNullValue());
|
assertThat(group.getQuery(), notNullValue());
|
||||||
|
|
||||||
|
@ -185,8 +189,8 @@ public class FileRolesStoreTests extends ESTestCase {
|
||||||
assertThat(role, notNullValue());
|
assertThat(role, notNullValue());
|
||||||
assertThat(role.name(), equalTo("role_query_fields"));
|
assertThat(role.name(), equalTo("role_query_fields"));
|
||||||
assertThat(role.cluster(), notNullValue());
|
assertThat(role.cluster(), notNullValue());
|
||||||
assertThat(role.cluster(), is(Permission.Cluster.Core.NONE));
|
assertThat(role.cluster(), is(ClusterPermission.Core.NONE));
|
||||||
assertThat(role.runAs(), is(Permission.RunAs.Core.NONE));
|
assertThat(role.runAs(), is(RunAsPermission.Core.NONE));
|
||||||
assertThat(role.indices(), notNullValue());
|
assertThat(role.indices(), notNullValue());
|
||||||
assertThat(role.indices().groups(), notNullValue());
|
assertThat(role.indices().groups(), notNullValue());
|
||||||
assertThat(role.indices().groups().length, is(1));
|
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().length, is(1));
|
||||||
assertThat(group.indices()[0], equalTo("query_fields_idx"));
|
assertThat(group.indices()[0], equalTo("query_fields_idx"));
|
||||||
assertThat(group.privilege(), notNullValue());
|
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.getFields(), contains("foo", "boo"));
|
||||||
assertThat(group.getQuery(), notNullValue());
|
assertThat(group.getQuery(), notNullValue());
|
||||||
}
|
}
|
||||||
|
@ -204,7 +208,7 @@ public class FileRolesStoreTests extends ESTestCase {
|
||||||
public void testParseFileWithFLSAndDLSDisabled() throws Exception {
|
public void testParseFileWithFLSAndDLSDisabled() throws Exception {
|
||||||
Path path = getDataPath("roles.yml");
|
Path path = getDataPath("roles.yml");
|
||||||
CapturingLogger logger = new CapturingLogger(CapturingLogger.Level.ERROR);
|
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());
|
logger, Settings.builder().put(ShieldPlugin.DLS_FLS_ENABLED_SETTING, false).build());
|
||||||
assertThat(roles, notNullValue());
|
assertThat(roles, notNullValue());
|
||||||
assertThat(roles.size(), is(7));
|
assertThat(roles.size(), is(7));
|
||||||
|
@ -224,7 +228,7 @@ public class FileRolesStoreTests extends ESTestCase {
|
||||||
*/
|
*/
|
||||||
public void testDefaultRolesFile() throws Exception {
|
public void testDefaultRolesFile() throws Exception {
|
||||||
Path path = getDataPath("default_roles.yml");
|
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, notNullValue());
|
||||||
assertThat(roles.size(), is(8));
|
assertThat(roles.size(), is(8));
|
||||||
|
|
||||||
|
@ -258,7 +262,7 @@ public class FileRolesStoreTests extends ESTestCase {
|
||||||
threadPool = new ThreadPool("test");
|
threadPool = new ThreadPool("test");
|
||||||
watcherService = new ResourceWatcherService(settings, threadPool);
|
watcherService = new ResourceWatcherService(settings, threadPool);
|
||||||
final CountDownLatch latch = new CountDownLatch(1);
|
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
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
|
@ -266,7 +270,7 @@ public class FileRolesStoreTests extends ESTestCase {
|
||||||
});
|
});
|
||||||
store.start();
|
store.start();
|
||||||
|
|
||||||
Permission.Global.Role role = store.role("role1");
|
Role role = store.role("role1");
|
||||||
assertThat(role, notNullValue());
|
assertThat(role, notNullValue());
|
||||||
role = store.role("role5");
|
role = store.role("role5");
|
||||||
assertThat(role, nullValue());
|
assertThat(role, nullValue());
|
||||||
|
@ -302,17 +306,17 @@ public class FileRolesStoreTests extends ESTestCase {
|
||||||
public void testThatEmptyFileDoesNotResultInLoop() throws Exception {
|
public void testThatEmptyFileDoesNotResultInLoop() throws Exception {
|
||||||
Path file = createTempFile();
|
Path file = createTempFile();
|
||||||
Files.write(file, Collections.singletonList("#"), StandardCharsets.UTF_8);
|
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()));
|
assertThat(roles.keySet(), is(empty()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testThatInvalidRoleDefinitions() throws Exception {
|
public void testThatInvalidRoleDefinitions() throws Exception {
|
||||||
Path path = getDataPath("invalid_roles.yml");
|
Path path = getDataPath("invalid_roles.yml");
|
||||||
CapturingLogger logger = new CapturingLogger(CapturingLogger.Level.ERROR);
|
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.size(), is(1));
|
||||||
assertThat(roles, hasKey("valid_role"));
|
assertThat(roles, hasKey("valid_role"));
|
||||||
Permission.Global.Role role = roles.get("valid_role");
|
Role role = roles.get("valid_role");
|
||||||
assertThat(role, notNullValue());
|
assertThat(role, notNullValue());
|
||||||
assertThat(role.name(), equalTo("valid_role"));
|
assertThat(role.name(), equalTo("valid_role"));
|
||||||
|
|
||||||
|
@ -338,20 +342,20 @@ public class FileRolesStoreTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReservedRoles() throws Exception {
|
public void testReservedRoles() throws Exception {
|
||||||
Set<Permission.Global.Role> reservedRoles = singleton(Permission.Global.Role.builder("reserved")
|
Set<Role> reservedRoles = singleton(Role.builder("reserved")
|
||||||
.cluster(Privilege.Cluster.ALL)
|
.cluster(ClusterPrivilege.ALL)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
CapturingLogger logger = new CapturingLogger(CapturingLogger.Level.INFO);
|
CapturingLogger logger = new CapturingLogger(CapturingLogger.Level.INFO);
|
||||||
|
|
||||||
Path path = getDataPath("reserved_roles.yml");
|
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, notNullValue());
|
||||||
assertThat(roles.size(), is(2));
|
assertThat(roles.size(), is(2));
|
||||||
|
|
||||||
assertThat(roles, hasKey("admin"));
|
assertThat(roles, hasKey("admin"));
|
||||||
assertThat(roles, hasKey("reserved"));
|
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);
|
List<CapturingLogger.Msg> messages = logger.output(CapturingLogger.Level.WARN);
|
||||||
assertThat(messages, notNullValue());
|
assertThat(messages, notNullValue());
|
||||||
|
@ -368,8 +372,8 @@ public class FileRolesStoreTests extends ESTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReservedRolesNonExistentRolesFile() throws Exception {
|
public void testReservedRolesNonExistentRolesFile() throws Exception {
|
||||||
Set<Permission.Global.Role> reservedRoles = singleton(Permission.Global.Role.builder("reserved")
|
Set<Role> reservedRoles = singleton(Role.builder("reserved")
|
||||||
.cluster(Privilege.Cluster.ALL)
|
.cluster(ClusterPrivilege.ALL)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
CapturingLogger logger = new CapturingLogger(CapturingLogger.Level.INFO);
|
CapturingLogger logger = new CapturingLogger(CapturingLogger.Level.INFO);
|
||||||
|
@ -377,12 +381,12 @@ public class FileRolesStoreTests extends ESTestCase {
|
||||||
Path path = createTempFile();
|
Path path = createTempFile();
|
||||||
Files.delete(path);
|
Files.delete(path);
|
||||||
assertThat(Files.exists(path), is(false));
|
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, notNullValue());
|
||||||
assertThat(roles.size(), is(1));
|
assertThat(roles.size(), is(1));
|
||||||
|
|
||||||
assertThat(roles, hasKey("reserved"));
|
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);
|
List<CapturingLogger.Msg> messages = logger.output(CapturingLogger.Level.WARN);
|
||||||
assertThat(messages, notNullValue());
|
assertThat(messages, notNullValue());
|
||||||
|
|
|
@ -6,8 +6,9 @@
|
||||||
package org.elasticsearch.watcher.shield;
|
package org.elasticsearch.watcher.shield;
|
||||||
|
|
||||||
import org.elasticsearch.shield.User;
|
import org.elasticsearch.shield.User;
|
||||||
import org.elasticsearch.shield.authz.Permission;
|
import org.elasticsearch.shield.authz.permission.Role;
|
||||||
import org.elasticsearch.shield.authz.Privilege;
|
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 InternalWatcherUser INSTANCE = new InternalWatcherUser(NAME, ROLE_NAMES);
|
||||||
|
|
||||||
public static final Permission.Global.Role ROLE = Permission.Global.Role.builder(ROLE_NAMES[0])
|
public static final Role ROLE = Role.builder(ROLE_NAMES[0])
|
||||||
.cluster(Privilege.Cluster.action("indices:admin/template/put"))
|
.cluster(ClusterPrivilege.action("indices:admin/template/put"))
|
||||||
|
|
||||||
// for now, the watches will be executed under the watcher user, meaning, all actions
|
// 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
|
// 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
|
// 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.
|
// it. this will require some work to attache/persist that user to/with the watch.
|
||||||
.add(Privilege.Index.ALL, "*")
|
.add(IndexPrivilege.ALL, "*")
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
InternalWatcherUser(String username, String[] roles) {
|
InternalWatcherUser(String username, String[] roles) {
|
||||||
|
|
|
@ -9,7 +9,7 @@ import org.elasticsearch.common.inject.AbstractModule;
|
||||||
import org.elasticsearch.common.logging.ESLogger;
|
import org.elasticsearch.common.logging.ESLogger;
|
||||||
import org.elasticsearch.common.logging.Loggers;
|
import org.elasticsearch.common.logging.Loggers;
|
||||||
import org.elasticsearch.common.settings.Settings;
|
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) {
|
void registerClusterPrivilege(String name, String... patterns) {
|
||||||
try {
|
try {
|
||||||
Privilege.Cluster.addCustom(name, patterns);
|
ClusterPrivilege.addCustom(name, patterns);
|
||||||
} catch (Exception se) {
|
} catch (Exception se) {
|
||||||
logger.warn("could not register cluster privilege [{}]", name);
|
logger.warn("could not register cluster privilege [{}]", name);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue