From 978996e08869d64c875807439797e0f5d51ad650 Mon Sep 17 00:00:00 2001 From: uboness Date: Thu, 21 Jan 2016 11:07:59 +0100 Subject: [PATCH 1/2] 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@8900f869e1972fc4d03c574950fb182e96a54b0a --- .../marvel/shield/InternalMarvelUser.java | 16 +- .../shield/action/ShieldActionFilter.java | 4 +- .../admin/ShieldInternalUserHolder.java | 12 +- .../shield/audit/index/IndexAuditTrail.java | 6 +- .../audit/index/IndexAuditUserHolder.java | 19 +- .../audit/logfile/LoggingAuditTrail.java | 5 +- .../shield/authz/AuthorizationModule.java | 9 +- .../authz/InternalAuthorizationService.java | 42 +- .../shield/authz/Permission.java | 656 ------------------ .../elasticsearch/shield/authz/Privilege.java | 468 ------------- .../shield/authz/SystemRole.java | 4 +- .../authz/esnative/ESNativeRolesStore.java | 3 +- .../authz/permission/ClusterPermission.java | 92 +++ .../authz/permission/GlobalPermission.java | 99 +++ .../authz/permission/IndicesPermission.java | 323 +++++++++ .../shield/authz/permission/Permission.java | 15 + .../shield/authz/permission/Role.java | 110 +++ .../authz/permission/RunAsPermission.java | 80 +++ .../privilege/AbstractAutomatonPrivilege.java | 80 +++ .../authz/privilege/ClusterPrivilege.java | 109 +++ .../authz/privilege/GeneralPrivilege.java | 39 ++ .../privilege/HealthAndStatsPrivilege.java | 23 + .../authz/privilege/IndexPrivilege.java | 149 ++++ .../shield/authz/privilege/Privilege.java | 114 +++ .../authz/privilege/SystemPrivilege.java | 42 ++ .../authz/store/CompositeRolesStore.java | 7 +- .../shield/authz/store/FileRolesStore.java | 46 +- .../shield/authz/store/RolesStore.java | 5 +- .../shield/admin/ESNativeTests.java | 6 +- .../InternalAuthorizationServiceTests.java | 50 +- .../accesscontrol/IndicesPermissionTests.java | 12 +- .../{ => permission}/PermissionTests.java | 35 +- .../authz/{ => privilege}/PrivilegeTests.java | 76 +- .../authz/store/FileRolesStoreTests.java | 100 +-- .../watcher/shield/InternalWatcherUser.java | 11 +- .../watcher/shield/WatcherShieldModule.java | 4 +- 36 files changed, 1523 insertions(+), 1348 deletions(-) delete mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/Permission.java delete mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/Privilege.java create mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/ClusterPermission.java create mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/GlobalPermission.java create mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/IndicesPermission.java create mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/Permission.java create mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/Role.java create mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/RunAsPermission.java create mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/AbstractAutomatonPrivilege.java create mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/ClusterPrivilege.java create mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/GeneralPrivilege.java create mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/HealthAndStatsPrivilege.java create mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/IndexPrivilege.java create mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/Privilege.java create mode 100644 elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/SystemPrivilege.java rename elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/{ => permission}/PermissionTests.java (67%) rename elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/{ => privilege}/PrivilegeTests.java (80%) diff --git a/elasticsearch/x-pack/marvel/src/main/java/org/elasticsearch/marvel/shield/InternalMarvelUser.java b/elasticsearch/x-pack/marvel/src/main/java/org/elasticsearch/marvel/shield/InternalMarvelUser.java index 06390acaa0d..4bed5475f52 100644 --- a/elasticsearch/x-pack/marvel/src/main/java/org/elasticsearch/marvel/shield/InternalMarvelUser.java +++ b/elasticsearch/x-pack/marvel/src/main/java/org/elasticsearch/marvel/shield/InternalMarvelUser.java @@ -9,8 +9,10 @@ import org.elasticsearch.action.admin.indices.template.get.GetIndexTemplatesActi import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction; import org.elasticsearch.marvel.agent.settings.MarvelSettings; import org.elasticsearch.shield.User; -import org.elasticsearch.shield.authz.Permission; -import org.elasticsearch.shield.authz.Privilege; +import org.elasticsearch.shield.authz.permission.Role; +import org.elasticsearch.shield.authz.privilege.ClusterPrivilege; +import org.elasticsearch.shield.authz.privilege.IndexPrivilege; +import org.elasticsearch.shield.authz.privilege.Privilege; /** * @@ -22,17 +24,17 @@ public class InternalMarvelUser extends User { public static final InternalMarvelUser INSTANCE = new InternalMarvelUser(NAME, ROLE_NAMES); - public static final Permission.Global.Role ROLE = Permission.Global.Role.builder(ROLE_NAMES[0]) - .cluster(Privilege.Cluster.get(new Privilege.Name( + public static final Role ROLE = Role.builder(ROLE_NAMES[0]) + .cluster(ClusterPrivilege.get(new Privilege.Name( PutIndexTemplateAction.NAME + "*", GetIndexTemplatesAction.NAME + "*", - Privilege.Cluster.MONITOR.name().toString()))) + ClusterPrivilege.MONITOR.name().toString()))) // we need all monitoring access - .add(Privilege.Index.MONITOR, "*") + .add(IndexPrivilege.MONITOR, "*") // and full access to .marvel-es-* and .marvel-es-data indices - .add(Privilege.Index.ALL, MarvelSettings.MARVEL_INDICES_PREFIX + "*") + .add(IndexPrivilege.ALL, MarvelSettings.MARVEL_INDICES_PREFIX + "*") // note, we don't need _license permission as we're taking the licenses // directly form the license service. diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/ShieldActionFilter.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/ShieldActionFilter.java index 4e221a98cf3..138589e61ba 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/ShieldActionFilter.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/action/ShieldActionFilter.java @@ -23,7 +23,7 @@ import org.elasticsearch.shield.action.interceptor.RequestInterceptor; import org.elasticsearch.shield.audit.AuditTrail; import org.elasticsearch.shield.authc.AuthenticationService; import org.elasticsearch.shield.authz.AuthorizationService; -import org.elasticsearch.shield.authz.Privilege; +import org.elasticsearch.shield.authz.privilege.HealthAndStatsPrivilege; import org.elasticsearch.shield.crypto.CryptoService; import org.elasticsearch.shield.license.ShieldLicenseState; import org.elasticsearch.tasks.Task; @@ -41,7 +41,7 @@ import static org.elasticsearch.shield.support.Exceptions.authorizationError; */ public class ShieldActionFilter extends AbstractComponent implements ActionFilter { - private static final Predicate LICENSE_EXPIRATION_ACTION_MATCHER = Privilege.HEALTH_AND_STATS.predicate(); + private static final Predicate LICENSE_EXPIRATION_ACTION_MATCHER = HealthAndStatsPrivilege.INSTANCE.predicate(); private final AuthenticationService authcService; private final AuthorizationService authzService; diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/admin/ShieldInternalUserHolder.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/admin/ShieldInternalUserHolder.java index 5bab75d3276..272e14f1889 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/admin/ShieldInternalUserHolder.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/admin/ShieldInternalUserHolder.java @@ -7,8 +7,10 @@ package org.elasticsearch.shield.admin; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction; import org.elasticsearch.shield.User; -import org.elasticsearch.shield.authz.Permission; -import org.elasticsearch.shield.authz.Privilege; +import org.elasticsearch.shield.authz.permission.Role; +import org.elasticsearch.shield.authz.privilege.ClusterPrivilege; +import org.elasticsearch.shield.authz.privilege.IndexPrivilege; +import org.elasticsearch.shield.authz.privilege.Privilege; /** * User holder for the shield internal user that manages the {@code .shield} @@ -19,9 +21,9 @@ public class ShieldInternalUserHolder { private static final String NAME = "__es_internal_user"; private static final String[] ROLES = new String[] { "__es_internal_role" }; - public static final Permission.Global.Role ROLE = Permission.Global.Role.builder(ROLES[0]) - .cluster(Privilege.Cluster.get(new Privilege.Name(PutIndexTemplateAction.NAME, "cluster:admin/shield/realm/cache/clear*", "cluster:admin/shield/roles/cache/clear*"))) - .add(Privilege.Index.ALL, ShieldTemplateService.SHIELD_ADMIN_INDEX_NAME) + public static final Role ROLE = Role.builder(ROLES[0]) + .cluster(ClusterPrivilege.get(new Privilege.Name(PutIndexTemplateAction.NAME, "cluster:admin/shield/realm/cache/clear*", "cluster:admin/shield/roles/cache/clear*"))) + .add(IndexPrivilege.ALL, ShieldTemplateService.SHIELD_ADMIN_INDEX_NAME) .build(); private static final User SHIELD_INTERNAL_USER = new User(NAME, ROLES); diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/index/IndexAuditTrail.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/index/IndexAuditTrail.java index 80e4524a5ed..6e05dc1e785 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/index/IndexAuditTrail.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/index/IndexAuditTrail.java @@ -43,18 +43,18 @@ import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.gateway.GatewayService; import org.elasticsearch.rest.RestRequest; import org.elasticsearch.shield.admin.ShieldInternalUserHolder; +import org.elasticsearch.shield.authz.privilege.SystemPrivilege; import org.elasticsearch.xpack.XPackPlugin; import org.elasticsearch.shield.User; import org.elasticsearch.shield.audit.AuditTrail; import org.elasticsearch.shield.authc.AuthenticationService; import org.elasticsearch.shield.authc.AuthenticationToken; -import org.elasticsearch.shield.authz.Privilege; +import org.elasticsearch.shield.authz.privilege.Privilege; import org.elasticsearch.shield.rest.RemoteHostHeader; import org.elasticsearch.shield.transport.filter.ShieldIpFilterRule; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.transport.Transport; import org.elasticsearch.transport.TransportMessage; -import org.elasticsearch.xpack.XPackPlugin; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; @@ -417,7 +417,7 @@ public class IndexAuditTrail extends AbstractComponent implements AuditTrail, Cl public void accessGranted(User user, String action, TransportMessage message) { if (!principalIsAuditor(user.principal())) { // special treatment for internal system actions - only log if explicitly told to - if ((user.isSystem() && Privilege.SYSTEM.predicate().test(action)) || ShieldInternalUserHolder.isShieldInternalUser(user)) { + if ((user.isSystem() && SystemPrivilege.INSTANCE.predicate().test(action)) || ShieldInternalUserHolder.isShieldInternalUser(user)) { if (events.contains(SYSTEM_ACCESS_GRANTED)) { try { enqueue(message("access_granted", action, user, indices(message), message), "access_granted"); diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/index/IndexAuditUserHolder.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/index/IndexAuditUserHolder.java index 3256121b16c..95237bd1501 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/index/IndexAuditUserHolder.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/index/IndexAuditUserHolder.java @@ -10,8 +10,9 @@ import org.elasticsearch.action.admin.indices.mapping.put.PutMappingAction; import org.elasticsearch.action.admin.indices.template.put.PutIndexTemplateAction; import org.elasticsearch.action.bulk.BulkAction; import org.elasticsearch.shield.User; -import org.elasticsearch.shield.authz.Permission; -import org.elasticsearch.shield.authz.Privilege; +import org.elasticsearch.shield.authz.permission.Role; +import org.elasticsearch.shield.authz.privilege.ClusterPrivilege; +import org.elasticsearch.shield.authz.privilege.IndexPrivilege; /** * @@ -20,13 +21,13 @@ public class IndexAuditUserHolder { private static final String NAME = "__indexing_audit_user"; private static final String[] ROLE_NAMES = new String[] { "__indexing_audit_role" }; - public static final Permission.Global.Role ROLE = Permission.Global.Role.builder(ROLE_NAMES[0]) - .cluster(Privilege.Cluster.action(PutIndexTemplateAction.NAME)) - .add(Privilege.Index.CREATE_INDEX, IndexAuditTrail.INDEX_NAME_PREFIX + "*") - .add(Privilege.Index.INDEX, IndexAuditTrail.INDEX_NAME_PREFIX + "*") - .add(Privilege.Index.action(IndicesExistsAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*") - .add(Privilege.Index.action(BulkAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*") - .add(Privilege.Index.action(PutMappingAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*") + public static final Role ROLE = Role.builder(ROLE_NAMES[0]) + .cluster(ClusterPrivilege.action(PutIndexTemplateAction.NAME)) + .add(IndexPrivilege.CREATE_INDEX, IndexAuditTrail.INDEX_NAME_PREFIX + "*") + .add(IndexPrivilege.INDEX, IndexAuditTrail.INDEX_NAME_PREFIX + "*") + .add(IndexPrivilege.action(IndicesExistsAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*") + .add(IndexPrivilege.action(BulkAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*") + .add(IndexPrivilege.action(PutMappingAction.NAME), IndexAuditTrail.INDEX_NAME_PREFIX + "*") .build(); private final User user; diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/logfile/LoggingAuditTrail.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/logfile/LoggingAuditTrail.java index 44a4c4fe73c..c5a9839b85b 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/logfile/LoggingAuditTrail.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/audit/logfile/LoggingAuditTrail.java @@ -20,7 +20,8 @@ import org.elasticsearch.shield.User; import org.elasticsearch.shield.admin.ShieldInternalUserHolder; import org.elasticsearch.shield.audit.AuditTrail; import org.elasticsearch.shield.authc.AuthenticationToken; -import org.elasticsearch.shield.authz.Privilege; +import org.elasticsearch.shield.authz.privilege.Privilege; +import org.elasticsearch.shield.authz.privilege.SystemPrivilege; import org.elasticsearch.shield.rest.RemoteHostHeader; import org.elasticsearch.shield.transport.filter.ShieldIpFilterRule; import org.elasticsearch.transport.Transport; @@ -195,7 +196,7 @@ public class LoggingAuditTrail extends AbstractLifecycleComponent reservedRoles = new HashSet<>(); + private final Set reservedRoles = new HashSet<>(); public AuthorizationModule(Settings settings) { super(settings); } - public void registerReservedRole(Permission.Global.Role role) { + public void registerReservedRole(Role role) { reservedRoles.add(role); } @Override protected void configureNode() { - Multibinder reservedRolesBinder = Multibinder.newSetBinder(binder(), Permission.Global.Role.class); - for (Permission.Global.Role reservedRole : reservedRoles) { + Multibinder reservedRolesBinder = Multibinder.newSetBinder(binder(), Role.class); + for (Role reservedRole : reservedRoles) { reservedRolesBinder.addBinding().toInstance(reservedRole); } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java index 2b42806de19..ffb390009f4 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/InternalAuthorizationService.java @@ -28,6 +28,12 @@ import org.elasticsearch.shield.authc.AuthenticationFailureHandler; import org.elasticsearch.shield.authz.accesscontrol.IndicesAccessControl; import org.elasticsearch.shield.authz.indicesresolver.DefaultIndicesAndAliasesResolver; import org.elasticsearch.shield.authz.indicesresolver.IndicesAndAliasesResolver; +import org.elasticsearch.shield.authz.permission.ClusterPermission; +import org.elasticsearch.shield.authz.permission.GlobalPermission; +import org.elasticsearch.shield.authz.permission.Role; +import org.elasticsearch.shield.authz.permission.RunAsPermission; +import org.elasticsearch.shield.authz.privilege.ClusterPrivilege; +import org.elasticsearch.shield.authz.privilege.IndexPrivilege; import org.elasticsearch.shield.authz.store.RolesStore; import org.elasticsearch.transport.TransportRequest; @@ -70,15 +76,15 @@ public class InternalAuthorizationService extends AbstractComponent implements A @Override public List authorizedIndicesAndAliases(User user, String action) { - String[] roles = user.roles(); - if (roles.length == 0) { + String[] rolesNames = user.roles(); + if (rolesNames.length == 0) { return Collections.emptyList(); } List> predicates = new ArrayList<>(); - for (String role : roles) { - Permission.Global.Role global = rolesStore.role(role); - if (global != null) { - predicates.add(global.indices().allowedIndicesMatcher(action)); + for (String roleName : rolesNames) { + Role role = rolesStore.role(roleName); + if (role != null) { + predicates.add(role.indices().allowedIndicesMatcher(action)); } } @@ -107,7 +113,7 @@ public class InternalAuthorizationService extends AbstractComponent implements A throw denial(user, action, request); } - Permission.Global permission = permission(user.roles()); + GlobalPermission permission = permission(user.roles()); final boolean isRunAs = user.runAs() != null; // permission can be null as it might be that the user's role // is unknown @@ -123,7 +129,7 @@ public class InternalAuthorizationService extends AbstractComponent implements A // check if the request is a run as request if (isRunAs) { // first we must authorize for the RUN_AS action - Permission.RunAs runAs = permission.runAs(); + RunAsPermission runAs = permission.runAs(); if (runAs != null && runAs.check(user.runAs().principal())) { grantRunAs(user, action, request); permission = permission(user.runAs().roles()); @@ -140,8 +146,8 @@ public class InternalAuthorizationService extends AbstractComponent implements A // first, we'll check if the action is a cluster action. If it is, we'll only check it // against the cluster permissions - if (Privilege.Cluster.ACTION_MATCHER.test(action)) { - Permission.Cluster cluster = permission.cluster(); + if (ClusterPrivilege.ACTION_MATCHER.test(action)) { + ClusterPermission cluster = permission.cluster(); if (cluster != null && cluster.check(action)) { request.putInContext(INDICES_PERMISSIONS_KEY, IndicesAccessControl.ALLOW_ALL); grant(user, action, request); @@ -151,7 +157,7 @@ public class InternalAuthorizationService extends AbstractComponent implements A } // ok... this is not a cluster action, let's verify it's an indices action - if (!Privilege.Index.ACTION_MATCHER.test(action)) { + if (!IndexPrivilege.ACTION_MATCHER.test(action)) { throw denial(user, action, request); } @@ -189,7 +195,7 @@ public class InternalAuthorizationService extends AbstractComponent implements A } //if we are creating an index we need to authorize potential aliases created at the same time - if (Privilege.Index.CREATE_INDEX_MATCHER.test(action)) { + if (IndexPrivilege.CREATE_INDEX_MATCHER.test(action)) { assert request instanceof CreateIndexRequest; Set aliases = ((CreateIndexRequest) request).aliases(); if (!aliases.isEmpty()) { @@ -209,21 +215,21 @@ public class InternalAuthorizationService extends AbstractComponent implements A grant(user, action, request); } - private Permission.Global permission(String[] roleNames) { + private GlobalPermission permission(String[] roleNames) { if (roleNames.length == 0) { - return Permission.Global.NONE; + return GlobalPermission.NONE; } if (roleNames.length == 1) { - Permission.Global.Role role = rolesStore.role(roleNames[0]); - return role == null ? Permission.Global.NONE : role; + Role role = rolesStore.role(roleNames[0]); + return role == null ? GlobalPermission.NONE : role; } // we'll take all the roles and combine their associated permissions - Permission.Global.Compound.Builder roles = Permission.Global.Compound.builder(); + GlobalPermission.Compound.Builder roles = GlobalPermission.Compound.builder(); for (String roleName : roleNames) { - Permission.Global role = rolesStore.role(roleName); + GlobalPermission role = rolesStore.role(roleName); if (role != null) { roles.add(role); } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/Permission.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/Permission.java deleted file mode 100644 index f387b410a71..00000000000 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/Permission.java +++ /dev/null @@ -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: - * - *
    - *
  • - * Cluster - a permission that is based on privileges for cluster wide actions - *
  • - *
  • - * Indices - a permission that is based on privileges for index related actions executed - * on specific indices - *
  • - *
  • RunAs - a permissions that is based on a general privilege that contains patterns of users that this - * user can execute a request as - *
  • - *
  • - * Global - a composite permission that combines a both cluster & indices permissions - *
  • - *
- */ -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 requestedIndicesOrAliases, MetaData metaData) { - Map 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 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 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 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 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 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 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 globals; - - public Globals(List 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 { - Map authorize(String action, Set requestedIndicesOrAliases, MetaData metaData); - - public static class Core implements Indices { - - public static final Core NONE = new Core() { - @Override - public Iterator iterator() { - return Collections.emptyIterator(); - } - - @Override - public boolean isEmpty() { - return true; - } - }; - - private final Function> loadingFunction; - - private final ConcurrentHashMap> allowedIndicesMatchersForAction = new ConcurrentHashMap<>(); - - private final Group[] groups; - - public Core(Group... groups) { - this.groups = groups; - loadingFunction = (action) -> { - List 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 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 allowedIndicesMatcher(String action) { - return allowedIndicesMatchersForAction.computeIfAbsent(action, loadingFunction); - } - - @Override - public Map authorize(String action, Set requestedIndicesOrAliases, MetaData metaData) { - // now... every index that is associated with the request, must be granted - // by at least one indices permission group - - SortedMap allAliasesAndIndices = metaData.getAliasAndIndexLookup(); - Map> rolesFieldsByIndex = new HashMap<>(); - Map> roleQueriesByIndex = new HashMap<>(); - Map grantedBuilder = new HashMap<>(); - - for (String indexOrAlias : requestedIndicesOrAliases) { - boolean granted = false; - Set 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 roleFields = rolesFieldsByIndex.get(index); - if (roleFields == null) { - roleFields = new HashSet<>(); - rolesFieldsByIndex.put(index, roleFields); - } - roleFields.addAll(group.getFields()); - } - if (group.getQuery() != null) { - Set 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 indexPermissions = new HashMap<>(); - for (Map.Entry entry : grantedBuilder.entrySet()) { - String index = entry.getKey(); - Set roleQueries = roleQueriesByIndex.get(index); - if (roleQueries != null) { - roleQueries = unmodifiableSet(roleQueries); - } - Set 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 globals; - - public Globals(List globals) { - this.globals = globals; - } - - @Override - public Iterator iterator() { - return globals == null || globals.isEmpty() ? - Collections.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 authorize(String action, Set requestedIndicesOrAliases, MetaData metaData) { - if (isEmpty()) { - return emptyMap(); - } - - // What this code does is just merge `IndexAccessControl` instances from the permissions this class holds: - Map indicesAccessControl = null; - for (Global permission : globals) { - Map temp = permission.indices().authorize(action, requestedIndicesOrAliases, metaData); - if (indicesAccessControl == null) { - indicesAccessControl = new HashMap<>(temp); - } else { - for (Map.Entry 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 { - - private final Iterator globals; - private Iterator current; - - Iter(List 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 actionMatcher; - private final String[] indices; - private final Predicate indexNameMatcher; - private final List fields; - private final BytesReference query; - - public Group(Privilege.Index privilege, @Nullable List 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 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 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 globals; - - public Globals(List 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; - } - } - } -} diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/Privilege.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/Privilege.java deleted file mode 100644 index cdf31bffa49..00000000000 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/Privilege.java +++ /dev/null @@ -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

> { - - 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 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 { - - private static final Predicate INTERNAL_PREDICATE = new AutomatonPredicate(patterns("internal:*")); - - protected static final Predicate 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 predicate() { - return PREDICATE; - } - - @Override - public boolean implies(System other) { - return true; - } - } - - public static class General extends AutomatonPrivilege { - - 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 { - - 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 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 ACTION_MATCHER = ALL.predicate(); - public static final Predicate CREATE_INDEX_MATCHER = CREATE_INDEX.predicate(); - - static Set values() { - return values; - } - - private static final ConcurrentHashMap 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 { - - 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 ACTION_MATCHER = Privilege.Cluster.ALL.predicate(); - - private static final Set values = new CopyOnWriteArraySet<>(); - static { - values.add(NONE); - values.add(ALL); - values.add(MONITOR); - values.add(MANAGE_SHIELD); - } - - static Set values() { - return values; - } - - private static final ConcurrentHashMap 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

> extends Privilege

{ - - 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 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 parts; - - public Name(String name) { - assert name != null && !name.contains(","); - parts = singleton(name); - } - - public Name(Set 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 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(); - } - } -} diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/SystemRole.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/SystemRole.java index bdec97abb4a..e99b2508a37 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/SystemRole.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/SystemRole.java @@ -5,6 +5,8 @@ */ package org.elasticsearch.shield.authz; +import org.elasticsearch.shield.authz.privilege.SystemPrivilege; + import java.util.function.Predicate; /** @@ -16,7 +18,7 @@ public class SystemRole { public static final String NAME = "__es_system_role"; - private static final Predicate PREDICATE = Privilege.SYSTEM.predicate(); + private static final Predicate PREDICATE = SystemPrivilege.INSTANCE.predicate(); private SystemRole() { } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/esnative/ESNativeRolesStore.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/esnative/ESNativeRolesStore.java index 7ecce142638..697a5e6ba1b 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/esnative/ESNativeRolesStore.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/esnative/ESNativeRolesStore.java @@ -29,7 +29,6 @@ import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.component.AbstractComponent; import org.elasticsearch.common.inject.Inject; import org.elasticsearch.common.inject.Provider; -import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.util.concurrent.AbstractRunnable; @@ -48,7 +47,7 @@ import org.elasticsearch.shield.action.authz.cache.ClearRolesCacheResponse; import org.elasticsearch.shield.admin.ShieldInternalUserHolder; import org.elasticsearch.shield.admin.ShieldTemplateService; import org.elasticsearch.shield.authc.AuthenticationService; -import org.elasticsearch.shield.authz.Permission.Global.Role; +import org.elasticsearch.shield.authz.permission.Role; import org.elasticsearch.shield.authz.RoleDescriptor; import org.elasticsearch.shield.authz.store.RolesStore; import org.elasticsearch.shield.client.ShieldClient; diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/ClusterPermission.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/ClusterPermission.java new file mode 100644 index 00000000000..11f7cb2c557 --- /dev/null +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/ClusterPermission.java @@ -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 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 globals; + + public Globals(List 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; + } + } + +} diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/GlobalPermission.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/GlobalPermission.java new file mode 100644 index 00000000000..2e585aea0fe --- /dev/null +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/GlobalPermission.java @@ -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 requestedIndicesOrAliases, MetaData metaData) { + Map 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 entry : indexPermissions.entrySet()) { + if (!entry.getValue().isGranted()) { + granted = false; + break; + } + } + return new IndicesAccessControl(granted, indexPermissions); + } + + public static class Compound extends GlobalPermission { + + public Compound(List 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 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)); + } + } + } +} diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/IndicesPermission.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/IndicesPermission.java new file mode 100644 index 00000000000..ec0b4e1345c --- /dev/null +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/IndicesPermission.java @@ -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 { + + Map authorize(String action, Set requestedIndicesOrAliases, MetaData metaData); + + public static class Core implements IndicesPermission { + + public static final Core NONE = new Core() { + @Override + public Iterator iterator() { + return Collections.emptyIterator(); + } + + @Override + public boolean isEmpty() { + return true; + } + }; + + private final Function> loadingFunction; + + private final ConcurrentHashMap> allowedIndicesMatchersForAction = new ConcurrentHashMap<>(); + + private final Group[] groups; + + public Core(Group... groups) { + this.groups = groups; + loadingFunction = (action) -> { + List 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 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 allowedIndicesMatcher(String action) { + return allowedIndicesMatchersForAction.computeIfAbsent(action, loadingFunction); + } + + @Override + public Map authorize(String action, Set requestedIndicesOrAliases, MetaData metaData) { + // now... every index that is associated with the request, must be granted + // by at least one indices permission group + + SortedMap allAliasesAndIndices = metaData.getAliasAndIndexLookup(); + Map> rolesFieldsByIndex = new HashMap<>(); + Map> roleQueriesByIndex = new HashMap<>(); + Map grantedBuilder = new HashMap<>(); + + for (String indexOrAlias : requestedIndicesOrAliases) { + boolean granted = false; + Set 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 roleFields = rolesFieldsByIndex.get(index); + if (roleFields == null) { + roleFields = new HashSet<>(); + rolesFieldsByIndex.put(index, roleFields); + } + roleFields.addAll(group.getFields()); + } + if (group.getQuery() != null) { + Set 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 indexPermissions = new HashMap<>(); + for (Map.Entry entry : grantedBuilder.entrySet()) { + String index = entry.getKey(); + Set roleQueries = roleQueriesByIndex.get(index); + if (roleQueries != null) { + roleQueries = unmodifiableSet(roleQueries); + } + Set 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 globals; + + public Globals(List globals) { + this.globals = globals; + } + + @Override + public Iterator iterator() { + return globals == null || globals.isEmpty() ? + Collections.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 authorize(String action, Set requestedIndicesOrAliases, MetaData metaData) { + if (isEmpty()) { + return emptyMap(); + } + + // What this code does is just merge `IndexAccessControl` instances from the permissions this class holds: + Map indicesAccessControl = null; + for (GlobalPermission permission : globals) { + Map temp = permission.indices().authorize(action, requestedIndicesOrAliases, metaData); + if (indicesAccessControl == null) { + indicesAccessControl = new HashMap<>(temp); + } else { + for (Map.Entry 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 { + + private final Iterator globals; + private Iterator current; + + Iter(List 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 actionMatcher; + private final String[] indices; + private final Predicate indexNameMatcher; + private final List fields; + private final BytesReference query; + + public Group(IndexPrivilege privilege, @Nullable List 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 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); + } + } +} diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/Permission.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/Permission.java new file mode 100644 index 00000000000..7eed0068f59 --- /dev/null +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/Permission.java @@ -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(); + +} diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/Role.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/Role.java new file mode 100644 index 00000000000..c6f831dd588 --- /dev/null +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/Role.java @@ -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 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 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); + } + } +} diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/RunAsPermission.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/RunAsPermission.java new file mode 100644 index 00000000000..7b14614c017 --- /dev/null +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/permission/RunAsPermission.java @@ -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 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 globals; + + public Globals(List 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; + } + } +} diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/AbstractAutomatonPrivilege.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/AbstractAutomatonPrivilege.java new file mode 100644 index 00000000000..3674184b459 --- /dev/null +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/AbstractAutomatonPrivilege.java @@ -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

> extends Privilege

{ + + 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 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(); + + +} diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/ClusterPrivilege.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/ClusterPrivilege.java new file mode 100644 index 00000000000..af6f3102bbf --- /dev/null +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/ClusterPrivilege.java @@ -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 { + + 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 ACTION_MATCHER = ClusterPrivilege.ALL.predicate(); + + private static final Set values = new CopyOnWriteArraySet<>(); + + static { + values.add(NONE); + values.add(ALL); + values.add(MONITOR); + values.add(MANAGE_SHIELD); + } + + static Set values() { + return values; + } + + private static final ConcurrentHashMap 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"); + } +} diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/GeneralPrivilege.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/GeneralPrivilege.java new file mode 100644 index 00000000000..b1884688996 --- /dev/null +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/GeneralPrivilege.java @@ -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 { + + 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; + } +} diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/HealthAndStatsPrivilege.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/HealthAndStatsPrivilege.java new file mode 100644 index 00000000000..0c975a4a1e5 --- /dev/null +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/HealthAndStatsPrivilege.java @@ -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*"); + } +} diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/IndexPrivilege.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/IndexPrivilege.java new file mode 100644 index 00000000000..b27be176982 --- /dev/null +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/IndexPrivilege.java @@ -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 { + + 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 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 ACTION_MATCHER = ALL.predicate(); + public static final Predicate CREATE_INDEX_MATCHER = CREATE_INDEX.predicate(); + + static Set values() { + return values; + } + + private static final ConcurrentHashMap 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"); + } + +} diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/Privilege.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/Privilege.java new file mode 100644 index 00000000000..f87c9426985 --- /dev/null +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/Privilege.java @@ -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

> { + + protected final Name name; + + Privilege(Name name) { + this.name = name; + } + + public Name name() { + return name; + } + + public abstract Predicate 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 parts; + + public Name(String name) { + assert name != null && !name.contains(","); + parts = singleton(name); + } + + public Name(Set 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 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(); + } + } +} diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/SystemPrivilege.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/SystemPrivilege.java new file mode 100644 index 00000000000..1e70e1aa344 --- /dev/null +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/privilege/SystemPrivilege.java @@ -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 { + + public static SystemPrivilege INSTANCE = new SystemPrivilege(); + + protected static final Predicate 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 predicate() { + return PREDICATE; + } + + @Override + public boolean implies(SystemPrivilege other) { + return true; + } +} diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/store/CompositeRolesStore.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/store/CompositeRolesStore.java index 4bdcae6deb4..3306f254a6a 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/store/CompositeRolesStore.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/store/CompositeRolesStore.java @@ -6,9 +6,8 @@ package org.elasticsearch.shield.authz.store; import org.elasticsearch.common.inject.Inject; -import org.elasticsearch.shield.authz.Permission; -import org.elasticsearch.shield.authz.RoleDescriptor; import org.elasticsearch.shield.authz.esnative.ESNativeRolesStore; +import org.elasticsearch.shield.authz.permission.Role; /** * A composite roles store that combines file-based and index-based roles @@ -25,9 +24,9 @@ public class CompositeRolesStore implements RolesStore { this.nativeRolesStore = nativeRolesStore; } - public Permission.Global.Role role(String role) { + public Role role(String role) { // Try the file first, then the index if it isn't there - Permission.Global.Role fileRole = fileRolesStore.role(role); + Role fileRole = fileRolesStore.role(role); if (fileRole != null) { return fileRole; } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/store/FileRolesStore.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/store/FileRolesStore.java index 9a5b081d465..c8dc316b04b 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/store/FileRolesStore.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/store/FileRolesStore.java @@ -22,9 +22,11 @@ import org.elasticsearch.common.xcontent.yaml.YamlXContent; import org.elasticsearch.env.Environment; import org.elasticsearch.shield.ShieldPlugin; import org.elasticsearch.shield.authc.support.RefreshListener; -import org.elasticsearch.shield.authz.Permission; -import org.elasticsearch.shield.authz.Privilege; -import org.elasticsearch.shield.authz.RoleDescriptor; +import org.elasticsearch.shield.authz.permission.Role; +import org.elasticsearch.shield.authz.privilege.ClusterPrivilege; +import org.elasticsearch.shield.authz.privilege.GeneralPrivilege; +import org.elasticsearch.shield.authz.privilege.IndexPrivilege; +import org.elasticsearch.shield.authz.privilege.Privilege; import org.elasticsearch.shield.authz.SystemRole; import org.elasticsearch.shield.support.NoOpLogger; import org.elasticsearch.shield.support.Validation; @@ -62,17 +64,17 @@ public class FileRolesStore extends AbstractLifecycleComponent imple private final Path file; private final RefreshListener listener; - private final Set reservedRoles; + private final Set reservedRoles; private final ResourceWatcherService watcherService; - private volatile Map permissions; + private volatile Map permissions; @Inject - public FileRolesStore(Settings settings, Environment env, ResourceWatcherService watcherService, Set reservedRoles) { + public FileRolesStore(Settings settings, Environment env, ResourceWatcherService watcherService, Set reservedRoles) { this(settings, env, watcherService, reservedRoles, RefreshListener.NOOP); } - public FileRolesStore(Settings settings, Environment env, ResourceWatcherService watcherService, Set reservedRoles, RefreshListener listener) { + public FileRolesStore(Settings settings, Environment env, ResourceWatcherService watcherService, Set reservedRoles, RefreshListener listener) { super(settings); this.file = resolveFile(settings, env); this.listener = listener; @@ -102,7 +104,7 @@ public class FileRolesStore extends AbstractLifecycleComponent imple } @Override - public Permission.Global.Role role(String role) { + public Role role(String role) { return permissions.get(role); } @@ -116,29 +118,29 @@ public class FileRolesStore extends AbstractLifecycleComponent imple } public static Set parseFileForRoleNames(Path path, ESLogger logger) { - Map roleMap = parseFile(path, Collections.emptySet(), logger, false, Settings.EMPTY); + Map roleMap = parseFile(path, Collections.emptySet(), logger, false, Settings.EMPTY); if (roleMap == null) { return emptySet(); } return roleMap.keySet(); } - public static Map parseFile(Path path, Set reservedRoles, ESLogger logger, Settings settings) { + public static Map parseFile(Path path, Set reservedRoles, ESLogger logger, Settings settings) { return parseFile(path, reservedRoles, logger, true, settings); } - public static Map parseFile(Path path, Set reservedRoles, ESLogger logger, boolean resolvePermission, Settings settings) { + public static Map parseFile(Path path, Set reservedRoles, ESLogger logger, boolean resolvePermission, Settings settings) { if (logger == null) { logger = NoOpLogger.INSTANCE; } - Map roles = new HashMap<>(); + Map roles = new HashMap<>(); logger.trace("attempted to read roles file located at [{}]", path.toAbsolutePath()); if (Files.exists(path)) { try { List roleSegments = roleSegments(path); for (String segment : roleSegments) { - Permission.Global.Role role = parseRole(segment, path, logger, resolvePermission, settings); + Role role = parseRole(segment, path, logger, resolvePermission, settings); if (role != null) { if (SystemRole.NAME.equals(role.name())) { logger.warn("role [{}] is reserved to the system. the relevant role definition in the mapping file will be ignored", SystemRole.NAME); @@ -154,7 +156,7 @@ public class FileRolesStore extends AbstractLifecycleComponent imple } // we now add all the fixed roles (overriding any attempts to override the fixed roles in the file) - for (Permission.Global.Role reservedRole : reservedRoles) { + for (Role reservedRole : reservedRoles) { if (roles.containsKey(reservedRole.name())) { logger.warn("role [{}] is reserved to the system. the relevant role definition in the mapping file will be ignored", reservedRole.name()); } @@ -164,7 +166,7 @@ public class FileRolesStore extends AbstractLifecycleComponent imple return unmodifiableMap(roles); } - private static Permission.Global.Role parseRole(String segment, Path path, ESLogger logger, boolean resolvePermissions, Settings settings) { + private static Role parseRole(String segment, Path path, ESLogger logger, boolean resolvePermissions, Settings settings) { String roleName = null; try { XContentParser parser = YamlXContent.yamlXContent.createParser(segment); @@ -179,9 +181,9 @@ public class FileRolesStore extends AbstractLifecycleComponent imple return null; } - Permission.Global.Role.Builder permission = Permission.Global.Role.builder(roleName); + Role.Builder role = Role.builder(roleName); if (resolvePermissions == false) { - return permission.build(); + return role.build(); } token = parser.nextToken(); @@ -216,7 +218,7 @@ public class FileRolesStore extends AbstractLifecycleComponent imple } if (name != null) { try { - permission.cluster(Privilege.Cluster.get(name)); + role.cluster(ClusterPrivilege.get(name)); } catch (IllegalArgumentException e) { logger.error("invalid role definition [{}] in roles file [{}]. could not resolve cluster privileges [{}]. skipping role...", roleName, path.toAbsolutePath(), name); return null; @@ -307,7 +309,7 @@ public class FileRolesStore extends AbstractLifecycleComponent imple } try { - permission.add(fields, query, Privilege.Index.get(name), indices); + role.add(fields, query, IndexPrivilege.get(name), indices); } catch (IllegalArgumentException e) { logger.error("invalid role definition [{}] in roles file [{}]. could not resolve indices privileges [{}]. skipping role...", roleName, path.toAbsolutePath(), name); return null; @@ -321,7 +323,7 @@ public class FileRolesStore extends AbstractLifecycleComponent imple } if (name != null) { try { - permission.add(Privilege.Index.get(name), indices); + role.add(IndexPrivilege.get(name), indices); } catch (IllegalArgumentException e) { logger.error("invalid role definition [{}] in roles file [{}]. could not resolve indices privileges [{}]. skipping role...", roleName, path.toAbsolutePath(), name); return null; @@ -358,7 +360,7 @@ public class FileRolesStore extends AbstractLifecycleComponent imple if (!names.isEmpty()) { Privilege.Name name = new Privilege.Name(names); try { - permission.runAs(new Privilege.General(new Privilege.Name(names), names.toArray(new String[names.size()]))); + role.runAs(new GeneralPrivilege(new Privilege.Name(names), names.toArray(new String[names.size()]))); } catch (IllegalArgumentException e) { logger.error("invalid role definition [{}] in roles file [{}]. could not resolve run_as privileges [{}]. skipping role...", roleName, path.toAbsolutePath(), name); return null; @@ -368,7 +370,7 @@ public class FileRolesStore extends AbstractLifecycleComponent imple logger.warn("unknown field [{}] found in role definition [{}] in roles file [{}]", currentFieldName, roleName, path.toAbsolutePath()); } } - return permission.build(); + return role.build(); } logger.error("invalid role definition [{}] in roles file [{}]. skipping role...", roleName, path.toAbsolutePath()); } diff --git a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/store/RolesStore.java b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/store/RolesStore.java index 2864c73806b..42a48a23145 100644 --- a/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/store/RolesStore.java +++ b/elasticsearch/x-pack/shield/src/main/java/org/elasticsearch/shield/authz/store/RolesStore.java @@ -5,14 +5,13 @@ */ package org.elasticsearch.shield.authz.store; -import org.elasticsearch.shield.authz.Permission; -import org.elasticsearch.shield.authz.RoleDescriptor; +import org.elasticsearch.shield.authz.permission.Role; /** * An interface for looking up a role given a string role name */ public interface RolesStore { - Permission.Global.Role role(String role); + Role role(String role); } diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/admin/ESNativeTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/admin/ESNativeTests.java index 6739a879dc5..46946464de0 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/admin/ESNativeTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/admin/ESNativeTests.java @@ -19,9 +19,9 @@ import org.elasticsearch.shield.action.admin.user.DeleteUserResponse; import org.elasticsearch.shield.action.admin.user.GetUsersResponse; import org.elasticsearch.shield.authc.esnative.ESNativeUsersStore; import org.elasticsearch.shield.authc.support.SecuredString; -import org.elasticsearch.shield.authz.Permission; import org.elasticsearch.shield.authz.RoleDescriptor; import org.elasticsearch.shield.authz.esnative.ESNativeRolesStore; +import org.elasticsearch.shield.authz.permission.Role; import org.elasticsearch.shield.client.ShieldClient; import org.elasticsearch.test.ShieldIntegTestCase; import org.elasticsearch.test.ShieldSettingsSource; @@ -322,7 +322,7 @@ public class ESNativeTests extends ShieldIntegTestCase { GetRolesResponse getRolesResponse = c.prepareGetRoles().roles("test_role").get(); assertTrue("test_role does not exist!", getRolesResponse.isExists()); assertTrue("any cluster permission should be authorized", - Permission.Global.Role.builder(getRolesResponse.roles().get(0)).build().cluster().check("cluster:admin/foo")); + Role.builder(getRolesResponse.roles().get(0)).build().cluster().check("cluster:admin/foo")); c.prepareAddRole() .name("test_role") .cluster("none") @@ -333,7 +333,7 @@ public class ESNativeTests extends ShieldIntegTestCase { assertTrue("test_role does not exist!", getRolesResponse.isExists()); assertFalse("no cluster permission should be authorized", - Permission.Global.Role.builder(getRolesResponse.roles().get(0)).build().cluster().check("cluster:admin/bar")); + Role.builder(getRolesResponse.roles().get(0)).build().cluster().check("cluster:admin/bar")); } } diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/InternalAuthorizationServiceTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/InternalAuthorizationServiceTests.java index 02a88854403..b89f442d757 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/InternalAuthorizationServiceTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/InternalAuthorizationServiceTests.java @@ -28,6 +28,10 @@ import org.elasticsearch.shield.User; import org.elasticsearch.shield.audit.AuditTrail; import org.elasticsearch.shield.authc.AnonymousService; import org.elasticsearch.shield.authc.DefaultAuthenticationFailureHandler; +import org.elasticsearch.shield.authz.permission.Role; +import org.elasticsearch.shield.authz.privilege.ClusterPrivilege; +import org.elasticsearch.shield.authz.privilege.GeneralPrivilege; +import org.elasticsearch.shield.authz.privilege.IndexPrivilege; import org.elasticsearch.shield.authz.store.RolesStore; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.transport.TransportRequest; @@ -139,7 +143,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { public void testThatNonIndicesAndNonClusterActionIsDenied() { TransportRequest request = mock(TransportRequest.class); User user = new User("test user", "a_all"); - when(rolesStore.role("a_all")).thenReturn(Permission.Global.Role.builder("a_role").add(Privilege.Index.ALL, "a").build()); + when(rolesStore.role("a_all")).thenReturn(Role.builder("a_role").add(IndexPrivilege.ALL, "a").build()); try { internalAuthorizationService.authorize(user, "whatever", request); @@ -154,7 +158,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { public void testThatRoleWithNoIndicesIsDenied() { TransportRequest request = new IndicesExistsRequest("a"); User user = new User("test user", "no_indices"); - when(rolesStore.role("no_indices")).thenReturn(Permission.Global.Role.builder("no_indices").cluster(Privilege.Cluster.action("")).build()); + when(rolesStore.role("no_indices")).thenReturn(Role.builder("no_indices").cluster(ClusterPrivilege.action("")).build()); try { internalAuthorizationService.authorize(user, "indices:a", request); @@ -168,7 +172,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { public void testScrollRelatedRequestsAllowed() { User user = new User("test user", "a_all"); - when(rolesStore.role("a_all")).thenReturn(Permission.Global.Role.builder("a_role").add(Privilege.Index.ALL, "a").build()); + when(rolesStore.role("a_all")).thenReturn(Role.builder("a_role").add(IndexPrivilege.ALL, "a").build()); ClearScrollRequest clearScrollRequest = new ClearScrollRequest(); internalAuthorizationService.authorize(user, ClearScrollAction.NAME, clearScrollRequest); @@ -201,7 +205,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { TransportRequest request = new IndicesExistsRequest("b"); ClusterState state = mock(ClusterState.class); User user = new User("test user", "a_all"); - when(rolesStore.role("a_all")).thenReturn(Permission.Global.Role.builder("a_all").add(Privilege.Index.ALL, "a").build()); + when(rolesStore.role("a_all")).thenReturn(Role.builder("a_all").add(IndexPrivilege.ALL, "a").build()); when(clusterService.state()).thenReturn(state); when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA); @@ -222,7 +226,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { request.alias(new Alias("a2")); ClusterState state = mock(ClusterState.class); User user = new User("test user", "a_all"); - when(rolesStore.role("a_all")).thenReturn(Permission.Global.Role.builder("a_all").add(Privilege.Index.ALL, "a").build()); + when(rolesStore.role("a_all")).thenReturn(Role.builder("a_all").add(IndexPrivilege.ALL, "a").build()); when(clusterService.state()).thenReturn(state); when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA); @@ -243,7 +247,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { request.alias(new Alias("a2")); ClusterState state = mock(ClusterState.class); User user = new User("test user", "a_all"); - when(rolesStore.role("a_all")).thenReturn(Permission.Global.Role.builder("a_all").add(Privilege.Index.ALL, "a", "a2").build()); + when(rolesStore.role("a_all")).thenReturn(Role.builder("a_all").add(IndexPrivilege.ALL, "a", "a2").build()); when(clusterService.state()).thenReturn(state); when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA); @@ -265,8 +269,8 @@ public class InternalAuthorizationServiceTests extends ESTestCase { public void testIndicesAliasesWithUserHavingRoles() { User user = new User("test user", "a_star", "b"); ClusterState state = mock(ClusterState.class); - when(rolesStore.role("a_star")).thenReturn(Permission.Global.Role.builder("a_star").add(Privilege.Index.ALL, "a*").build()); - when(rolesStore.role("b")).thenReturn(Permission.Global.Role.builder("a_star").add(Privilege.Index.SEARCH, "b").build()); + when(rolesStore.role("a_star")).thenReturn(Role.builder("a_star").add(IndexPrivilege.ALL, "a*").build()); + when(rolesStore.role("b")).thenReturn(Role.builder("a_star").add(IndexPrivilege.SEARCH, "b").build()); when(clusterService.state()).thenReturn(state); Settings indexSettings = Settings.builder().put("index.version.created", Version.CURRENT).build(); when(state.metaData()).thenReturn(MetaData.builder() @@ -295,7 +299,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { AnonymousService anonymousService = new AnonymousService(Settings.builder().put("shield.authc.anonymous.roles", "a_all").build()); internalAuthorizationService = new InternalAuthorizationService(Settings.EMPTY, rolesStore, clusterService, auditTrail, anonymousService, new DefaultAuthenticationFailureHandler()); - when(rolesStore.role("a_all")).thenReturn(Permission.Global.Role.builder("a_all").add(Privilege.Index.ALL, "a").build()); + when(rolesStore.role("a_all")).thenReturn(Role.builder("a_all").add(IndexPrivilege.ALL, "a").build()); when(clusterService.state()).thenReturn(state); when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA); @@ -320,7 +324,7 @@ public class InternalAuthorizationServiceTests extends ESTestCase { .build()); internalAuthorizationService = new InternalAuthorizationService(Settings.EMPTY, rolesStore, clusterService, auditTrail, anonymousService, new DefaultAuthenticationFailureHandler()); - when(rolesStore.role("a_all")).thenReturn(Permission.Global.Role.builder("a_all").add(Privilege.Index.ALL, "a").build()); + when(rolesStore.role("a_all")).thenReturn(Role.builder("a_all").add(IndexPrivilege.ALL, "a").build()); when(clusterService.state()).thenReturn(state); when(state.metaData()).thenReturn(MetaData.EMPTY_META_DATA); @@ -354,10 +358,10 @@ public class InternalAuthorizationServiceTests extends ESTestCase { TransportRequest request = mock(TransportRequest.class); User user = new User("test user", new String[] { "can run as" }, new User("run as me", new String[] { "doesn't exist" })); assertThat(user.runAs(), is(notNullValue())); - when(rolesStore.role("can run as")).thenReturn(Permission.Global.Role + when(rolesStore.role("can run as")).thenReturn(Role .builder("can run as") - .runAs(new Privilege.General("", "not the right user")) - .add(Privilege.Index.ALL, "a") + .runAs(new GeneralPrivilege("", "not the right user")) + .add(IndexPrivilege.ALL, "a") .build()); try { @@ -374,10 +378,10 @@ public class InternalAuthorizationServiceTests extends ESTestCase { TransportRequest request = new IndicesExistsRequest("a"); User user = new User("test user", new String[] { "can run as" }, new User("run as me", new String[] { "b" })); assertThat(user.runAs(), is(notNullValue())); - when(rolesStore.role("can run as")).thenReturn(Permission.Global.Role + when(rolesStore.role("can run as")).thenReturn(Role .builder("can run as") - .runAs(new Privilege.General("", "run as me")) - .add(Privilege.Index.ALL, "a") + .runAs(new GeneralPrivilege("", "run as me")) + .add(IndexPrivilege.ALL, "a") .build()); if (randomBoolean()) { @@ -388,9 +392,9 @@ public class InternalAuthorizationServiceTests extends ESTestCase { .settings(Settings.builder().put("index.version.created", Version.CURRENT).build()) .numberOfShards(1).numberOfReplicas(0).build(), true) .build()); - when(rolesStore.role("b")).thenReturn(Permission.Global.Role + when(rolesStore.role("b")).thenReturn(Role .builder("b") - .add(Privilege.Index.ALL, "b") + .add(IndexPrivilege.ALL, "b") .build()); } @@ -409,10 +413,10 @@ public class InternalAuthorizationServiceTests extends ESTestCase { TransportRequest request = new IndicesExistsRequest("b"); User user = new User("test user", new String[] { "can run as" }, new User("run as me", new String[] { "b" })); assertThat(user.runAs(), is(notNullValue())); - when(rolesStore.role("can run as")).thenReturn(Permission.Global.Role + when(rolesStore.role("can run as")).thenReturn(Role .builder("can run as") - .runAs(new Privilege.General("", "run as me")) - .add(Privilege.Index.ALL, "a") + .runAs(new GeneralPrivilege("", "run as me")) + .add(IndexPrivilege.ALL, "a") .build()); ClusterState state = mock(ClusterState.class); when(clusterService.state()).thenReturn(state); @@ -421,9 +425,9 @@ public class InternalAuthorizationServiceTests extends ESTestCase { .settings(Settings.builder().put("index.version.created", Version.CURRENT).build()) .numberOfShards(1).numberOfReplicas(0).build(), true) .build()); - when(rolesStore.role("b")).thenReturn(Permission.Global.Role + when(rolesStore.role("b")).thenReturn(Role .builder("b") - .add(Privilege.Index.ALL, "b") + .add(IndexPrivilege.ALL, "b") .build()); internalAuthorizationService.authorize(user, "indices:a", request); diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/accesscontrol/IndicesPermissionTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/accesscontrol/IndicesPermissionTests.java index d74700de4e8..f4d1f2b9332 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/accesscontrol/IndicesPermissionTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/accesscontrol/IndicesPermissionTests.java @@ -14,8 +14,8 @@ import org.elasticsearch.common.bytes.BytesArray; import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.util.set.Sets; -import org.elasticsearch.shield.authz.Permission; -import org.elasticsearch.shield.authz.Privilege; +import org.elasticsearch.shield.authz.permission.Role; +import org.elasticsearch.shield.authz.privilege.IndexPrivilege; import org.elasticsearch.test.ESTestCase; import java.util.Arrays; @@ -40,7 +40,7 @@ public class IndicesPermissionTests extends ESTestCase { // basics: BytesReference query = new BytesArray("{}"); List fields = Arrays.asList("_field"); - Permission.Global.Role role = Permission.Global.Role.builder("_role").add(fields, query, Privilege.Index.ALL, "_index").build(); + Role role = Role.builder("_role").add(fields, query, IndexPrivilege.ALL, "_index").build(); IndicesAccessControl permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md); assertThat(permissions.getIndexPermissions("_index"), notNullValue()); assertThat(permissions.getIndexPermissions("_index").getFields().size(), equalTo(1)); @@ -49,7 +49,7 @@ public class IndicesPermissionTests extends ESTestCase { assertThat(permissions.getIndexPermissions("_index").getQueries().iterator().next(), equalTo(query)); // no document level security: - role = Permission.Global.Role.builder("_role").add(fields, null, Privilege.Index.ALL, "_index").build(); + role = Role.builder("_role").add(fields, null, IndexPrivilege.ALL, "_index").build(); permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md); assertThat(permissions.getIndexPermissions("_index"), notNullValue()); assertThat(permissions.getIndexPermissions("_index").getFields().size(), equalTo(1)); @@ -57,7 +57,7 @@ public class IndicesPermissionTests extends ESTestCase { assertThat(permissions.getIndexPermissions("_index").getQueries(), nullValue()); // no field level security: - role = Permission.Global.Role.builder("_role").add(null, query, Privilege.Index.ALL, "_index").build(); + role = Role.builder("_role").add(null, query, IndexPrivilege.ALL, "_index").build(); permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_index"), md); assertThat(permissions.getIndexPermissions("_index"), notNullValue()); assertThat(permissions.getIndexPermissions("_index").getFields(), nullValue()); @@ -65,7 +65,7 @@ public class IndicesPermissionTests extends ESTestCase { assertThat(permissions.getIndexPermissions("_index").getQueries().iterator().next(), equalTo(query)); // index group associated with an alias: - role = Permission.Global.Role.builder("_role").add(fields, query, Privilege.Index.ALL, "_alias").build(); + role = Role.builder("_role").add(fields, query, IndexPrivilege.ALL, "_alias").build(); permissions = role.authorize(SearchAction.NAME, Sets.newHashSet("_alias"), md); assertThat(permissions.getIndexPermissions("_index"), notNullValue()); assertThat(permissions.getIndexPermissions("_index").getFields().size(), equalTo(1)); diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/PermissionTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/PermissionTests.java similarity index 67% rename from elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/PermissionTests.java rename to elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/PermissionTests.java index 3c8d6db0ff4..941126e4ba2 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/PermissionTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/permission/PermissionTests.java @@ -3,10 +3,11 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.shield.authz; +package org.elasticsearch.shield.authz.permission; import org.elasticsearch.action.get.GetAction; -import org.elasticsearch.shield.authz.Privilege.Cluster; +import org.elasticsearch.shield.authz.privilege.GeneralPrivilege; +import org.elasticsearch.shield.authz.privilege.ClusterPrivilege; import org.elasticsearch.test.ESTestCase; import org.junit.Before; @@ -15,10 +16,10 @@ import java.util.Collections; import java.util.Iterator; import java.util.function.Predicate; -import static org.elasticsearch.shield.authz.Privilege.Index.MONITOR; -import static org.elasticsearch.shield.authz.Privilege.Index.READ; -import static org.elasticsearch.shield.authz.Privilege.Index.SEARCH; -import static org.elasticsearch.shield.authz.Privilege.Index.union; +import static org.elasticsearch.shield.authz.privilege.IndexPrivilege.MONITOR; +import static org.elasticsearch.shield.authz.privilege.IndexPrivilege.READ; +import static org.elasticsearch.shield.authz.privilege.IndexPrivilege.SEARCH; +import static org.elasticsearch.shield.authz.privilege.IndexPrivilege.union; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; @@ -27,11 +28,11 @@ import static org.hamcrest.Matchers.notNullValue; * */ public class PermissionTests extends ESTestCase { - private Permission.Global.Role permission; + private Role permission; @Before public void init() { - Permission.Global.Role.Builder builder = Permission.Global.Role.builder("test"); + Role.Builder builder = Role.builder("test"); builder.add(union(SEARCH, MONITOR), "test_*", "/foo.*/"); builder.add(union(READ), "baz_*foo", "/fool.*bar/"); builder.add(union(MONITOR), "/bar.*/"); @@ -49,12 +50,12 @@ public class PermissionTests extends ESTestCase { } public void testIndicesGlobalsIterator() { - Permission.Global.Role.Builder builder = Permission.Global.Role.builder("tc_role"); - builder.cluster(Cluster.action("cluster:monitor/nodes/info")); - Permission.Global.Role noIndicesPermission = builder.build(); + Role.Builder builder = Role.builder("tc_role"); + builder.cluster(ClusterPrivilege.action("cluster:monitor/nodes/info")); + Role noIndicesPermission = builder.build(); - Permission.Indices.Globals indicesGlobals = new Permission.Indices.Globals(Collections.unmodifiableList(Arrays.asList(noIndicesPermission, permission))); - Iterator iterator = indicesGlobals.iterator(); + IndicesPermission.Globals indicesGlobals = new IndicesPermission.Globals(Collections.unmodifiableList(Arrays.asList(noIndicesPermission, permission))); + Iterator iterator = indicesGlobals.iterator(); assertThat(iterator.hasNext(), is(equalTo(true))); int count = 0; while (iterator.hasNext()) { @@ -65,8 +66,8 @@ public class PermissionTests extends ESTestCase { } public void testBuildEmptyRole() { - Permission.Global.Role.Builder permission = Permission.Global.Role.builder("some_role"); - Permission.Global.Role role = permission.build(); + Role.Builder permission = Role.builder("some_role"); + Role role = permission.build(); assertThat(role, notNullValue()); assertThat(role.cluster(), notNullValue()); assertThat(role.indices(), notNullValue()); @@ -74,8 +75,8 @@ public class PermissionTests extends ESTestCase { } public void testRunAs() { - Permission.Global.Role permission = Permission.Global.Role.builder("some_role") - .runAs(new Privilege.General("name", "user1", "run*")) + Role permission = Role.builder("some_role") + .runAs(new GeneralPrivilege("name", "user1", "run*")) .build(); assertThat(permission.runAs().check("user1"), is(true)); assertThat(permission.runAs().check("user"), is(false)); diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/PrivilegeTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/privilege/PrivilegeTests.java similarity index 80% rename from elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/PrivilegeTests.java rename to elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/privilege/PrivilegeTests.java index d35963ae316..7b5aff1374d 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/PrivilegeTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/privilege/PrivilegeTests.java @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -package org.elasticsearch.shield.authz; +package org.elasticsearch.shield.authz.privilege; import org.elasticsearch.action.get.GetAction; import org.elasticsearch.action.get.MultiGetAction; @@ -47,7 +47,7 @@ public class PrivilegeTests extends ESTestCase { } public void testSubActionPattern() throws Exception { - AutomatonPredicate predicate = new AutomatonPredicate(Automatons.patterns("foo" + Privilege.SUB_ACTION_SUFFIX_PATTERN)); + AutomatonPredicate predicate = new AutomatonPredicate(Automatons.patterns("foo*")); assertThat(predicate.test("foo[n][nodes]"), is(true)); assertThat(predicate.test("foo[n]"), is(true)); assertThat(predicate.test("bar[n][nodes]"), is(false)); @@ -56,36 +56,36 @@ public class PrivilegeTests extends ESTestCase { public void testCluster() throws Exception { Privilege.Name name = new Privilege.Name("monitor"); - Privilege.Cluster cluster = Privilege.Cluster.get(name); - assertThat(cluster, is(Privilege.Cluster.MONITOR)); + ClusterPrivilege cluster = ClusterPrivilege.get(name); + assertThat(cluster, is(ClusterPrivilege.MONITOR)); // since "all" implies "monitor", this should collapse to All name = new Privilege.Name("monitor", "all"); - cluster = Privilege.Cluster.get(name); - assertThat(cluster, is(Privilege.Cluster.ALL)); + cluster = ClusterPrivilege.get(name); + assertThat(cluster, is(ClusterPrivilege.ALL)); name = new Privilege.Name("monitor", "none"); - cluster = Privilege.Cluster.get(name); - assertThat(cluster, is(Privilege.Cluster.MONITOR)); + cluster = ClusterPrivilege.get(name); + assertThat(cluster, is(ClusterPrivilege.MONITOR)); Privilege.Name name2 = new Privilege.Name("none", "monitor"); - Privilege.Cluster cluster2 = Privilege.Cluster.get(name2); + ClusterPrivilege cluster2 = ClusterPrivilege.get(name2); assertThat(cluster, is(cluster2)); } public void testClusterTemplateActions() throws Exception { Privilege.Name name = new Privilege.Name("indices:admin/template/delete"); - Privilege.Cluster cluster = Privilege.Cluster.get(name); + ClusterPrivilege cluster = ClusterPrivilege.get(name); assertThat(cluster, notNullValue()); assertThat(cluster.predicate().test("indices:admin/template/delete"), is(true)); name = new Privilege.Name("indices:admin/template/get"); - cluster = Privilege.Cluster.get(name); + cluster = ClusterPrivilege.get(name); assertThat(cluster, notNullValue()); assertThat(cluster.predicate().test("indices:admin/template/get"), is(true)); name = new Privilege.Name("indices:admin/template/put"); - cluster = Privilege.Cluster.get(name); + cluster = ClusterPrivilege.get(name); assertThat(cluster, notNullValue()); assertThat(cluster.predicate().test("indices:admin/template/put"), is(true)); } @@ -93,28 +93,28 @@ public class PrivilegeTests extends ESTestCase { public void testClusterInvalidName() throws Exception { thrown.expect(IllegalArgumentException.class); Privilege.Name actionName = new Privilege.Name("foobar"); - Privilege.Cluster.get(actionName); + ClusterPrivilege.get(actionName); } public void testClusterAction() throws Exception { Privilege.Name actionName = new Privilege.Name("cluster:admin/snapshot/delete"); - Privilege.Cluster cluster = Privilege.Cluster.get(actionName); + ClusterPrivilege cluster = ClusterPrivilege.get(actionName); assertThat(cluster, notNullValue()); assertThat(cluster.predicate().test("cluster:admin/snapshot/delete"), is(true)); assertThat(cluster.predicate().test("cluster:admin/snapshot/dele"), is(false)); } public void testClusterAddCustom() throws Exception { - Privilege.Cluster.addCustom("foo", "cluster:bar"); + ClusterPrivilege.addCustom("foo", "cluster:bar"); boolean found = false; - for (Privilege.Cluster cluster : Privilege.Cluster.values()) { + for (ClusterPrivilege cluster : ClusterPrivilege.values()) { if ("foo".equals(cluster.name.toString())) { found = true; assertThat(cluster.predicate().test("cluster:bar"), is(true)); } } assertThat(found, is(true)); - Privilege.Cluster cluster = Privilege.Cluster.get(new Privilege.Name("foo")); + ClusterPrivilege cluster = ClusterPrivilege.get(new Privilege.Name("foo")); assertThat(cluster, notNullValue()); assertThat(cluster.name().toString(), is("foo")); assertThat(cluster.predicate().test("cluster:bar"), is(true)); @@ -122,7 +122,7 @@ public class PrivilegeTests extends ESTestCase { public void testClusterAddCustomInvalidPattern() throws Exception { try { - Privilege.Cluster.addCustom("foo", "bar"); + ClusterPrivilege.addCustom("foo", "bar"); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e) { assertThat(e.getMessage(), containsString("cannot register custom cluster privilege [foo]")); @@ -132,7 +132,7 @@ public class PrivilegeTests extends ESTestCase { public void testClusterAddCustomAlreadyExists() throws Exception { try { - Privilege.Cluster.addCustom("all", "bar"); + ClusterPrivilege.addCustom("all", "bar"); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e) { assertThat(e.getMessage(), containsString("cannot register custom cluster privilege [all]")); @@ -142,19 +142,19 @@ public class PrivilegeTests extends ESTestCase { public void testIndexAction() throws Exception { Privilege.Name actionName = new Privilege.Name("indices:admin/mapping/delete"); - Privilege.Index index = Privilege.Index.get(actionName); + IndexPrivilege index = IndexPrivilege.get(actionName); assertThat(index, notNullValue()); assertThat(index.predicate().test("indices:admin/mapping/delete"), is(true)); assertThat(index.predicate().test("indices:admin/mapping/dele"), is(false)); } public void testIndexCollapse() throws Exception { - Privilege.Index[] values = Privilege.Index.values().toArray(new Privilege.Index[Privilege.Index.values().size()]); - Privilege.Index first = values[randomIntBetween(0, values.length-1)]; - Privilege.Index second = values[randomIntBetween(0, values.length-1)]; + IndexPrivilege[] values = IndexPrivilege.values().toArray(new IndexPrivilege[IndexPrivilege.values().size()]); + IndexPrivilege first = values[randomIntBetween(0, values.length-1)]; + IndexPrivilege second = values[randomIntBetween(0, values.length-1)]; Privilege.Name name = new Privilege.Name(first.name().toString(), second.name().toString()); - Privilege.Index index = Privilege.Index.get(name); + IndexPrivilege index = IndexPrivilege.get(name); if (first.implies(second)) { assertThat(index, is(first)); @@ -166,12 +166,12 @@ public class PrivilegeTests extends ESTestCase { } public void testIndexImplies() throws Exception { - Privilege.Index[] values = Privilege.Index.values().toArray(new Privilege.Index[Privilege.Index.values().size()]); - Privilege.Index first = values[randomIntBetween(0, values.length-1)]; - Privilege.Index second = values[randomIntBetween(0, values.length-1)]; + IndexPrivilege[] values = IndexPrivilege.values().toArray(new IndexPrivilege[IndexPrivilege.values().size()]); + IndexPrivilege first = values[randomIntBetween(0, values.length-1)]; + IndexPrivilege second = values[randomIntBetween(0, values.length-1)]; Privilege.Name name = new Privilege.Name(first.name().toString(), second.name().toString()); - Privilege.Index index = Privilege.Index.get(name); + IndexPrivilege index = IndexPrivilege.get(name); assertThat(index.implies(first), is(true)); assertThat(index.implies(second), is(true)); @@ -182,13 +182,13 @@ public class PrivilegeTests extends ESTestCase { if (second.implies(first)) { if (index != second) { - Privilege.Index idx = Privilege.Index.get(name); + IndexPrivilege idx = IndexPrivilege.get(name); idx.name().toString(); } assertThat(index, is(second)); } - for (Privilege.Index other : Privilege.Index.values()) { + for (IndexPrivilege other : IndexPrivilege.values()) { if (first.implies(other) || second.implies(other) || index.isAlias(other)) { assertThat("index privilege [" + index + "] should imply [" + other + "]", index.implies(other), is(true)); } else if (other.implies(first) && other.implies(second)) { @@ -198,16 +198,16 @@ public class PrivilegeTests extends ESTestCase { } public void testIndexAddCustom() throws Exception { - Privilege.Index.addCustom("foo", "indices:bar"); + IndexPrivilege.addCustom("foo", "indices:bar"); boolean found = false; - for (Privilege.Index index : Privilege.Index.values()) { + for (IndexPrivilege index : IndexPrivilege.values()) { if ("foo".equals(index.name.toString())) { found = true; assertThat(index.predicate().test("indices:bar"), is(true)); } } assertThat(found, is(true)); - Privilege.Index index = Privilege.Index.get(new Privilege.Name("foo")); + IndexPrivilege index = IndexPrivilege.get(new Privilege.Name("foo")); assertThat(index, notNullValue()); assertThat(index.name().toString(), is("foo")); assertThat(index.predicate().test("indices:bar"), is(true)); @@ -215,7 +215,7 @@ public class PrivilegeTests extends ESTestCase { public void testIndexAddCustomInvalidPattern() throws Exception { try { - Privilege.Index.addCustom("foo", "bar"); + IndexPrivilege.addCustom("foo", "bar"); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e) { assertThat(e.getMessage(), containsString("cannot register custom index privilege [foo]")); @@ -225,7 +225,7 @@ public class PrivilegeTests extends ESTestCase { public void testIndexAddCustomAlreadyExists() throws Exception { try { - Privilege.Index.addCustom("all", "bar"); + IndexPrivilege.addCustom("all", "bar"); fail("Expected IllegalArgumentException"); } catch (IllegalArgumentException e) { assertThat(e.getMessage(), containsString("cannot register custom index privilege [all]")); @@ -234,7 +234,7 @@ public class PrivilegeTests extends ESTestCase { } public void testSystem() throws Exception { - Predicate predicate = Privilege.SYSTEM.predicate(); + Predicate predicate = SystemPrivilege.INSTANCE.predicate(); assertThat(predicate.test("indices:monitor/whatever"), is(true)); assertThat(predicate.test("cluster:monitor/whatever"), is(true)); assertThat(predicate.test("cluster:admin/snapshot/status[nodes]"), is(false)); @@ -250,7 +250,7 @@ public class PrivilegeTests extends ESTestCase { } public void testSearchPrivilege() throws Exception { - Predicate predicate = Privilege.Index.SEARCH.predicate(); + Predicate predicate = IndexPrivilege.SEARCH.predicate(); assertThat(predicate.test(SearchAction.NAME), is(true)); assertThat(predicate.test(SearchAction.NAME + "/whatever"), is(true)); assertThat(predicate.test(MultiSearchAction.NAME), is(true)); @@ -265,7 +265,7 @@ public class PrivilegeTests extends ESTestCase { } public void testGetPrivilege() throws Exception { - Predicate predicate = Privilege.Index.GET.predicate(); + Predicate predicate = IndexPrivilege.GET.predicate(); assertThat(predicate.test(GetAction.NAME), is(true)); assertThat(predicate.test(GetAction.NAME + "/whatever"), is(true)); assertThat(predicate.test(MultiGetAction.NAME), is(true)); diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/store/FileRolesStoreTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/store/FileRolesStoreTests.java index c42eef59e66..b6abff41796 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/store/FileRolesStoreTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/shield/authz/store/FileRolesStoreTests.java @@ -10,8 +10,12 @@ import org.elasticsearch.env.Environment; import org.elasticsearch.shield.ShieldPlugin; import org.elasticsearch.shield.audit.logfile.CapturingLogger; import org.elasticsearch.shield.authc.support.RefreshListener; -import org.elasticsearch.shield.authz.Permission; -import org.elasticsearch.shield.authz.Privilege; +import org.elasticsearch.shield.authz.permission.ClusterPermission; +import org.elasticsearch.shield.authz.permission.IndicesPermission; +import org.elasticsearch.shield.authz.permission.Role; +import org.elasticsearch.shield.authz.permission.RunAsPermission; +import org.elasticsearch.shield.authz.privilege.ClusterPrivilege; +import org.elasticsearch.shield.authz.privilege.IndexPrivilege; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.threadpool.ThreadPool; import org.elasticsearch.watcher.ResourceWatcherService; @@ -48,86 +52,86 @@ import static org.hamcrest.Matchers.startsWith; public class FileRolesStoreTests extends ESTestCase { public void testParseFile() throws Exception { Path path = getDataPath("roles.yml"); - Map roles = FileRolesStore.parseFile(path, Collections.emptySet(), + Map roles = FileRolesStore.parseFile(path, Collections.emptySet(), logger, Settings.builder().put(ShieldPlugin.DLS_FLS_ENABLED_SETTING, true).build()); assertThat(roles, notNullValue()); assertThat(roles.size(), is(10)); - Permission.Global.Role role = roles.get("role1"); + Role role = roles.get("role1"); assertThat(role, notNullValue()); assertThat(role.name(), equalTo("role1")); assertThat(role.cluster(), notNullValue()); - assertThat(role.cluster().privilege(), is(Privilege.Cluster.ALL)); + assertThat(role.cluster().privilege(), is(ClusterPrivilege.ALL)); assertThat(role.indices(), notNullValue()); assertThat(role.indices().groups(), notNullValue()); assertThat(role.indices().groups().length, is(2)); - assertThat(role.runAs(), is(Permission.RunAs.Core.NONE)); + assertThat(role.runAs(), is(RunAsPermission.Core.NONE)); - Permission.Global.Indices.Group group = role.indices().groups()[0]; + IndicesPermission.Group group = role.indices().groups()[0]; assertThat(group.indices(), notNullValue()); assertThat(group.indices().length, is(2)); assertThat(group.indices()[0], equalTo("idx1")); assertThat(group.indices()[1], equalTo("idx2")); assertThat(group.privilege(), notNullValue()); - assertThat(group.privilege(), is(Privilege.Index.READ)); + assertThat(group.privilege(), is(IndexPrivilege.READ)); group = role.indices().groups()[1]; assertThat(group.indices(), notNullValue()); assertThat(group.indices().length, is(1)); assertThat(group.indices()[0], equalTo("idx3")); assertThat(group.privilege(), notNullValue()); - assertThat(group.privilege(), is(Privilege.Index.CRUD)); + assertThat(group.privilege(), is(IndexPrivilege.CRUD)); role = roles.get("role1.ab"); assertThat(role, notNullValue()); assertThat(role.name(), equalTo("role1.ab")); assertThat(role.cluster(), notNullValue()); - assertThat(role.cluster().privilege(), is(Privilege.Cluster.ALL)); + assertThat(role.cluster().privilege(), is(ClusterPrivilege.ALL)); assertThat(role.indices(), notNullValue()); assertThat(role.indices().groups(), notNullValue()); assertThat(role.indices().groups().length, is(0)); - assertThat(role.runAs(), is(Permission.RunAs.Core.NONE)); + assertThat(role.runAs(), is(RunAsPermission.Core.NONE)); role = roles.get("role2"); assertThat(role, notNullValue()); assertThat(role.name(), equalTo("role2")); assertThat(role.cluster(), notNullValue()); - assertThat(role.cluster().privilege(), is(Privilege.Cluster.ALL)); // MONITOR is collapsed into ALL + assertThat(role.cluster().privilege(), is(ClusterPrivilege.ALL)); // MONITOR is collapsed into ALL assertThat(role.indices(), notNullValue()); - assertThat(role.indices(), is(Permission.Indices.Core.NONE)); - assertThat(role.runAs(), is(Permission.RunAs.Core.NONE)); + assertThat(role.indices(), is(IndicesPermission.Core.NONE)); + assertThat(role.runAs(), is(RunAsPermission.Core.NONE)); role = roles.get("role3"); assertThat(role, notNullValue()); assertThat(role.name(), equalTo("role3")); assertThat(role.cluster(), notNullValue()); - assertThat(role.cluster(), is(Permission.Cluster.Core.NONE)); + assertThat(role.cluster(), is(ClusterPermission.Core.NONE)); assertThat(role.indices(), notNullValue()); assertThat(role.indices().groups(), notNullValue()); assertThat(role.indices().groups().length, is(1)); - assertThat(role.runAs(), is(Permission.RunAs.Core.NONE)); + assertThat(role.runAs(), is(RunAsPermission.Core.NONE)); group = role.indices().groups()[0]; assertThat(group.indices(), notNullValue()); assertThat(group.indices().length, is(1)); assertThat(group.indices()[0], equalTo("/.*_.*/")); assertThat(group.privilege(), notNullValue()); - assertThat(group.privilege().isAlias(Privilege.Index.union(Privilege.Index.READ, Privilege.Index.WRITE)), is(true)); + assertThat(group.privilege().isAlias(IndexPrivilege.union(IndexPrivilege.READ, IndexPrivilege.WRITE)), is(true)); role = roles.get("role4"); assertThat(role, notNullValue()); assertThat(role.name(), equalTo("role4")); assertThat(role.cluster(), notNullValue()); - assertThat(role.cluster(), is(Permission.Cluster.Core.NONE)); - assertThat(role.indices(), is(Permission.Indices.Core.NONE)); - assertThat(role.runAs(), is(Permission.RunAs.Core.NONE)); + assertThat(role.cluster(), is(ClusterPermission.Core.NONE)); + assertThat(role.indices(), is(IndicesPermission.Core.NONE)); + assertThat(role.runAs(), is(RunAsPermission.Core.NONE)); role = roles.get("role_run_as"); assertThat(role, notNullValue()); assertThat(role.name(), equalTo("role_run_as")); assertThat(role.cluster(), notNullValue()); - assertThat(role.cluster(), is(Permission.Cluster.Core.NONE)); - assertThat(role.indices(), is(Permission.Indices.Core.NONE)); + assertThat(role.cluster(), is(ClusterPermission.Core.NONE)); + assertThat(role.indices(), is(IndicesPermission.Core.NONE)); assertThat(role.runAs(), notNullValue()); assertThat(role.runAs().check("user1"), is(true)); assertThat(role.runAs().check("user2"), is(true)); @@ -137,8 +141,8 @@ public class FileRolesStoreTests extends ESTestCase { assertThat(role, notNullValue()); assertThat(role.name(), equalTo("role_run_as1")); assertThat(role.cluster(), notNullValue()); - assertThat(role.cluster(), is(Permission.Cluster.Core.NONE)); - assertThat(role.indices(), is(Permission.Indices.Core.NONE)); + assertThat(role.cluster(), is(ClusterPermission.Core.NONE)); + assertThat(role.indices(), is(IndicesPermission.Core.NONE)); assertThat(role.runAs(), notNullValue()); assertThat(role.runAs().check("user1"), is(true)); assertThat(role.runAs().check("user2"), is(true)); @@ -148,8 +152,8 @@ public class FileRolesStoreTests extends ESTestCase { assertThat(role, notNullValue()); assertThat(role.name(), equalTo("role_fields")); assertThat(role.cluster(), notNullValue()); - assertThat(role.cluster(), is(Permission.Cluster.Core.NONE)); - assertThat(role.runAs(), is(Permission.RunAs.Core.NONE)); + assertThat(role.cluster(), is(ClusterPermission.Core.NONE)); + assertThat(role.runAs(), is(RunAsPermission.Core.NONE)); assertThat(role.indices(), notNullValue()); assertThat(role.indices().groups(), notNullValue()); assertThat(role.indices().groups().length, is(1)); @@ -159,15 +163,15 @@ public class FileRolesStoreTests extends ESTestCase { assertThat(group.indices().length, is(1)); assertThat(group.indices()[0], equalTo("field_idx")); assertThat(group.privilege(), notNullValue()); - assertThat(group.privilege().isAlias(Privilege.Index.READ), is(true)); + assertThat(group.privilege().isAlias(IndexPrivilege.READ), is(true)); assertThat(group.getFields(), contains("foo", "boo")); role = roles.get("role_query"); assertThat(role, notNullValue()); assertThat(role.name(), equalTo("role_query")); assertThat(role.cluster(), notNullValue()); - assertThat(role.cluster(), is(Permission.Cluster.Core.NONE)); - assertThat(role.runAs(), is(Permission.RunAs.Core.NONE)); + assertThat(role.cluster(), is(ClusterPermission.Core.NONE)); + assertThat(role.runAs(), is(RunAsPermission.Core.NONE)); assertThat(role.indices(), notNullValue()); assertThat(role.indices().groups(), notNullValue()); assertThat(role.indices().groups().length, is(1)); @@ -177,7 +181,7 @@ public class FileRolesStoreTests extends ESTestCase { assertThat(group.indices().length, is(1)); assertThat(group.indices()[0], equalTo("query_idx")); assertThat(group.privilege(), notNullValue()); - assertThat(group.privilege().isAlias(Privilege.Index.READ), is(true)); + assertThat(group.privilege().isAlias(IndexPrivilege.READ), is(true)); assertThat(group.getFields(), nullValue()); assertThat(group.getQuery(), notNullValue()); @@ -185,8 +189,8 @@ public class FileRolesStoreTests extends ESTestCase { assertThat(role, notNullValue()); assertThat(role.name(), equalTo("role_query_fields")); assertThat(role.cluster(), notNullValue()); - assertThat(role.cluster(), is(Permission.Cluster.Core.NONE)); - assertThat(role.runAs(), is(Permission.RunAs.Core.NONE)); + assertThat(role.cluster(), is(ClusterPermission.Core.NONE)); + assertThat(role.runAs(), is(RunAsPermission.Core.NONE)); assertThat(role.indices(), notNullValue()); assertThat(role.indices().groups(), notNullValue()); assertThat(role.indices().groups().length, is(1)); @@ -196,7 +200,7 @@ public class FileRolesStoreTests extends ESTestCase { assertThat(group.indices().length, is(1)); assertThat(group.indices()[0], equalTo("query_fields_idx")); assertThat(group.privilege(), notNullValue()); - assertThat(group.privilege().isAlias(Privilege.Index.READ), is(true)); + assertThat(group.privilege().isAlias(IndexPrivilege.READ), is(true)); assertThat(group.getFields(), contains("foo", "boo")); assertThat(group.getQuery(), notNullValue()); } @@ -204,7 +208,7 @@ public class FileRolesStoreTests extends ESTestCase { public void testParseFileWithFLSAndDLSDisabled() throws Exception { Path path = getDataPath("roles.yml"); CapturingLogger logger = new CapturingLogger(CapturingLogger.Level.ERROR); - Map roles = FileRolesStore.parseFile(path, Collections.emptySet(), + Map roles = FileRolesStore.parseFile(path, Collections.emptySet(), logger, Settings.builder().put(ShieldPlugin.DLS_FLS_ENABLED_SETTING, false).build()); assertThat(roles, notNullValue()); assertThat(roles.size(), is(7)); @@ -224,7 +228,7 @@ public class FileRolesStoreTests extends ESTestCase { */ public void testDefaultRolesFile() throws Exception { Path path = getDataPath("default_roles.yml"); - Map roles = FileRolesStore.parseFile(path, Collections.emptySet(), logger, Settings.EMPTY); + Map roles = FileRolesStore.parseFile(path, Collections.emptySet(), logger, Settings.EMPTY); assertThat(roles, notNullValue()); assertThat(roles.size(), is(8)); @@ -258,7 +262,7 @@ public class FileRolesStoreTests extends ESTestCase { threadPool = new ThreadPool("test"); watcherService = new ResourceWatcherService(settings, threadPool); final CountDownLatch latch = new CountDownLatch(1); - FileRolesStore store = new FileRolesStore(settings, env, watcherService, Collections.emptySet(), new RefreshListener() { + FileRolesStore store = new FileRolesStore(settings, env, watcherService, Collections.emptySet(), new RefreshListener() { @Override public void onRefresh() { latch.countDown(); @@ -266,7 +270,7 @@ public class FileRolesStoreTests extends ESTestCase { }); store.start(); - Permission.Global.Role role = store.role("role1"); + Role role = store.role("role1"); assertThat(role, notNullValue()); role = store.role("role5"); assertThat(role, nullValue()); @@ -302,17 +306,17 @@ public class FileRolesStoreTests extends ESTestCase { public void testThatEmptyFileDoesNotResultInLoop() throws Exception { Path file = createTempFile(); Files.write(file, Collections.singletonList("#"), StandardCharsets.UTF_8); - Map roles = FileRolesStore.parseFile(file, Collections.emptySet(), logger, Settings.EMPTY); + Map roles = FileRolesStore.parseFile(file, Collections.emptySet(), logger, Settings.EMPTY); assertThat(roles.keySet(), is(empty())); } public void testThatInvalidRoleDefinitions() throws Exception { Path path = getDataPath("invalid_roles.yml"); CapturingLogger logger = new CapturingLogger(CapturingLogger.Level.ERROR); - Map roles = FileRolesStore.parseFile(path, Collections.emptySet(), logger, Settings.EMPTY); + Map roles = FileRolesStore.parseFile(path, Collections.emptySet(), logger, Settings.EMPTY); assertThat(roles.size(), is(1)); assertThat(roles, hasKey("valid_role")); - Permission.Global.Role role = roles.get("valid_role"); + Role role = roles.get("valid_role"); assertThat(role, notNullValue()); assertThat(role.name(), equalTo("valid_role")); @@ -338,20 +342,20 @@ public class FileRolesStoreTests extends ESTestCase { } public void testReservedRoles() throws Exception { - Set reservedRoles = singleton(Permission.Global.Role.builder("reserved") - .cluster(Privilege.Cluster.ALL) + Set reservedRoles = singleton(Role.builder("reserved") + .cluster(ClusterPrivilege.ALL) .build()); CapturingLogger logger = new CapturingLogger(CapturingLogger.Level.INFO); Path path = getDataPath("reserved_roles.yml"); - Map roles = FileRolesStore.parseFile(path, reservedRoles, logger, Settings.EMPTY); + Map roles = FileRolesStore.parseFile(path, reservedRoles, logger, Settings.EMPTY); assertThat(roles, notNullValue()); assertThat(roles.size(), is(2)); assertThat(roles, hasKey("admin")); assertThat(roles, hasKey("reserved")); - Permission.Global.Role reserved = roles.get("reserved"); + Role reserved = roles.get("reserved"); List messages = logger.output(CapturingLogger.Level.WARN); assertThat(messages, notNullValue()); @@ -368,8 +372,8 @@ public class FileRolesStoreTests extends ESTestCase { } public void testReservedRolesNonExistentRolesFile() throws Exception { - Set reservedRoles = singleton(Permission.Global.Role.builder("reserved") - .cluster(Privilege.Cluster.ALL) + Set reservedRoles = singleton(Role.builder("reserved") + .cluster(ClusterPrivilege.ALL) .build()); CapturingLogger logger = new CapturingLogger(CapturingLogger.Level.INFO); @@ -377,12 +381,12 @@ public class FileRolesStoreTests extends ESTestCase { Path path = createTempFile(); Files.delete(path); assertThat(Files.exists(path), is(false)); - Map roles = FileRolesStore.parseFile(path, reservedRoles, logger, Settings.EMPTY); + Map roles = FileRolesStore.parseFile(path, reservedRoles, logger, Settings.EMPTY); assertThat(roles, notNullValue()); assertThat(roles.size(), is(1)); assertThat(roles, hasKey("reserved")); - Permission.Global.Role reserved = roles.get("reserved"); + Role reserved = roles.get("reserved"); List messages = logger.output(CapturingLogger.Level.WARN); assertThat(messages, notNullValue()); diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/shield/InternalWatcherUser.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/shield/InternalWatcherUser.java index 2e65328e168..1a4243e94f0 100644 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/shield/InternalWatcherUser.java +++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/shield/InternalWatcherUser.java @@ -6,8 +6,9 @@ package org.elasticsearch.watcher.shield; import org.elasticsearch.shield.User; -import org.elasticsearch.shield.authz.Permission; -import org.elasticsearch.shield.authz.Privilege; +import org.elasticsearch.shield.authz.permission.Role; +import org.elasticsearch.shield.authz.privilege.ClusterPrivilege; +import org.elasticsearch.shield.authz.privilege.IndexPrivilege; /** * @@ -19,8 +20,8 @@ public class InternalWatcherUser extends User { public static final InternalWatcherUser INSTANCE = new InternalWatcherUser(NAME, ROLE_NAMES); - public static final Permission.Global.Role ROLE = Permission.Global.Role.builder(ROLE_NAMES[0]) - .cluster(Privilege.Cluster.action("indices:admin/template/put")) + public static final Role ROLE = Role.builder(ROLE_NAMES[0]) + .cluster(ClusterPrivilege.action("indices:admin/template/put")) // for now, the watches will be executed under the watcher user, meaning, all actions // taken as part of the execution will be executed on behalf of this user. this includes @@ -29,7 +30,7 @@ public class InternalWatcherUser extends User { // // at later phases we'll want to execute the watch on behalf of the user who registers // it. this will require some work to attache/persist that user to/with the watch. - .add(Privilege.Index.ALL, "*") + .add(IndexPrivilege.ALL, "*") .build(); InternalWatcherUser(String username, String[] roles) { diff --git a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/shield/WatcherShieldModule.java b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/shield/WatcherShieldModule.java index 9a9f62bee23..ad1a6a8bddc 100644 --- a/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/shield/WatcherShieldModule.java +++ b/elasticsearch/x-pack/watcher/src/main/java/org/elasticsearch/watcher/shield/WatcherShieldModule.java @@ -9,7 +9,7 @@ import org.elasticsearch.common.inject.AbstractModule; import org.elasticsearch.common.logging.ESLogger; import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.shield.authz.Privilege; +import org.elasticsearch.shield.authz.privilege.ClusterPrivilege; /** * @@ -31,7 +31,7 @@ public class WatcherShieldModule extends AbstractModule { void registerClusterPrivilege(String name, String... patterns) { try { - Privilege.Cluster.addCustom(name, patterns); + ClusterPrivilege.addCustom(name, patterns); } catch (Exception se) { logger.warn("could not register cluster privilege [{}]", name); From 8d67195ffca02acfe0b81f4ed40cebc6d3ba19bf Mon Sep 17 00:00:00 2001 From: jaymode Date: Mon, 25 Jan 2016 14:38:17 -0500 Subject: [PATCH 2/2] test: add additional logging to debug ClearRolesCacheTests CI failures See elastic/elasticsearch#1354 Original commit: elastic/x-pack-elasticsearch@a7cbf5e08c5857074beabb82e36d2a18f8e084f7 --- .../integration/ClearRolesCacheTests.java | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/integration/ClearRolesCacheTests.java b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/integration/ClearRolesCacheTests.java index 9e07441ceac..29ea1dca777 100644 --- a/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/integration/ClearRolesCacheTests.java +++ b/elasticsearch/x-pack/shield/src/test/java/org/elasticsearch/integration/ClearRolesCacheTests.java @@ -12,7 +12,6 @@ import org.elasticsearch.common.Strings; import org.elasticsearch.common.network.NetworkModule; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.node.Node; import org.elasticsearch.rest.RestStatus; import org.elasticsearch.shield.action.admin.role.AddRoleResponse; import org.elasticsearch.shield.action.admin.role.GetRolesResponse; @@ -42,6 +41,7 @@ import static org.hamcrest.Matchers.notNullValue; * Test for the Shield clear roles API that changes the polling aspect of shield to only run once an hour in order to * test the cache clearing APIs. */ +@TestLogging("shield.authc.esnative:TRACE,shield.authz.esnative:TRACE,integration:DEBUG") public class ClearRolesCacheTests extends ShieldIntegTestCase { private static String[] roles; @@ -85,6 +85,7 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase { .cluster("none") .addIndices(new String[] { "*" }, new String[] { "ALL" }, null, null) .get(); + logger.debug("--> created role [{}]", role); } ensureYellow(ShieldTemplateService.SHIELD_ADMIN_INDEX_NAME); @@ -106,13 +107,13 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase { .build(); } - @TestLogging("_root:DEBUG") public void testModifyingViaApiClearsCache() throws Exception { Client client = internalCluster().transportClient(); ShieldClient shieldClient = new ShieldClient(client); int modifiedRolesCount = randomIntBetween(1, roles.length); List toModify = randomSubsetOf(modifiedRolesCount, roles); + logger.debug("--> modifying roles {} to have run_as", toModify); for (String role : toModify) { AddRoleResponse response = shieldClient.prepareAddRole().name(role) .cluster("none") @@ -120,6 +121,7 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase { .runAs(role) .get(); assertThat(response.isCreated(), is(false)); + logger.debug("--> updated role [{}] with run_as", role); } assertRolesAreCorrect(shieldClient, toModify); @@ -130,12 +132,14 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase { int modifiedRolesCount = randomIntBetween(1, roles.length); List toModify = randomSubsetOf(modifiedRolesCount, roles); + logger.debug("--> modifying roles {} to have run_as", toModify); for (String role : toModify) { UpdateResponse response = client.prepareUpdate().setId(role).setIndex(ShieldTemplateService.SHIELD_ADMIN_INDEX_NAME) .setType(ESNativeRolesStore.INDEX_ROLE_TYPE) .setDoc("run_as", new String[] { role }) .get(); assertThat(response.isCreated(), is(false)); + logger.debug("--> updated role [{}] with run_as", role); } ShieldClient shieldClient = new ShieldClient(client); @@ -143,11 +147,12 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase { GetRolesResponse roleResponse = shieldClient.prepareGetRoles().roles(role).get(); assertThat(roleResponse.isExists(), is(true)); final String[] runAs = roleResponse.roles().get(0).getRunAs(); - assertThat("role should be cached and no rules have run as set", runAs == null || runAs.length == 0, is(true)); + assertThat("role [" + role + "] should be cached and no rules have run as set", runAs == null || runAs.length == 0, is(true)); } - boolean useHttp = randomBoolean(); - boolean clearAll = randomBoolean(); + final boolean useHttp = randomBoolean(); + final boolean clearAll = randomBoolean(); + logger.debug("--> starting to clear roles. using http [{}] clearing all [{}]", useHttp, clearAll); String[] rolesToClear = clearAll ? (randomBoolean() ? roles : null) : toModify.toArray(Strings.EMPTY_ARRAY); if (useHttp) { String path; @@ -176,6 +181,7 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase { final String role = randomFrom(roles); List foundRoles = shieldClient.prepareGetRoles().roles(role).get().roles(); assertThat(foundRoles.size(), is(1)); + logger.debug("--> deleting role [{}]", role); DeleteResponse response = client.prepareDelete(ShieldTemplateService.SHIELD_ADMIN_INDEX_NAME, ESNativeRolesStore.INDEX_ROLE_TYPE, role).get(); assertThat(response.isFound(), is(true)); @@ -189,14 +195,15 @@ public class ClearRolesCacheTests extends ShieldIntegTestCase { private void assertRolesAreCorrect(ShieldClient shieldClient, List toModify) { for (String role : roles) { + logger.debug("--> getting role [{}]", role); GetRolesResponse roleResponse = shieldClient.prepareGetRoles().roles(role).get(); assertThat(roleResponse.isExists(), is(true)); final String[] runAs = roleResponse.roles().get(0).getRunAs(); if (toModify.contains(role)) { - assertThat("role should be modified and have run as", runAs == null || runAs.length == 0, is(false)); + assertThat("role [" + role + "] should be modified and have run as", runAs == null || runAs.length == 0, is(false)); assertThat(Arrays.asList(runAs).contains(role), is(true)); } else { - assertThat("role should be cached and no rules have run as set", runAs == null || runAs.length == 0, is(true)); + assertThat("role [" + role + "] should be cached and not have run as set but does!", runAs == null || runAs.length == 0, is(true)); } } }