diff options
author | Jon Marius Venstad <jvenstad@yahoo-inc.com> | 2019-03-28 16:26:03 +0100 |
---|---|---|
committer | Jon Marius Venstad <jvenstad@yahoo-inc.com> | 2019-03-28 16:26:03 +0100 |
commit | a499fee25892dd10c0db2d692a56c066113b9203 (patch) | |
tree | a4adf038f054eca9b6abe8bc6465bd0168e98c41 /controller-server | |
parent | bc7890c555e761a819889b181b2d8c8c1c9dce90 (diff) |
More splits, and new roles, with inheritance, and renaming
Diffstat (limited to 'controller-server')
6 files changed, 158 insertions, 103 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleResolver.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleResolver.java index 8a5e70af6c0..21c9875bb8b 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleResolver.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleResolver.java @@ -93,7 +93,7 @@ public class AthenzRoleResolver implements RoleMembership.Resolver { memberships.add(Role.hostedOperator); } if (tenant.isPresent() && isTenantAdmin(identity, tenant.get())) { - memberships.add(Role.tenantAdmin).limitedTo(tenant.get().name()); + memberships.add(Role.athenzTenantAdmin).limitedTo(tenant.get().name()); } AthenzDomain principalDomain = identity.getDomain(); if (principalDomain.equals(SCREWDRIVER_DOMAIN)) { @@ -102,11 +102,11 @@ public class AthenzRoleResolver implements RoleMembership.Resolver { if (tenant.get() instanceof AthenzTenant) { AthenzDomain tenantDomain = ((AthenzTenant) tenant.get()).domain(); if (hasDeployerAccess(identity, tenantDomain, application.get())) { - memberships.add(Role.tenantPipelineOperator).limitedTo(tenant.get().name(), application.get()); + memberships.add(Role.tenantPipeline).limitedTo(tenant.get().name(), application.get()); } } else { - memberships.add(Role.tenantPipelineOperator).limitedTo(tenant.get().name(), application.get()); + memberships.add(Role.tenantPipeline).limitedTo(tenant.get().name(), application.get()); } } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/PathGroup.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/PathGroup.java index 2ece7e62ab9..680712c8b9a 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/PathGroup.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/PathGroup.java @@ -30,47 +30,65 @@ public enum PathGroup { userManagement("/user/v1/{*}"), // TODO probably add tenant and application levels. /** Paths used for creating user tenants. */ - onboardingUser("/application/v4/user"), + user("/application/v4/user"), /** Paths used for creating tenants with proper access control. */ - onboarding("/application/v4/tenant/{ignored}"), + tenant(Matcher.tenant, + "/application/v4/tenant/{tenant}"), /** Paths used by tenant administrators. */ - tenant(Matcher.tenant, - "/application/v4/tenant/{tenant}", - "/application/v4/tenant/{tenant}/application/"), + tenantInfo(Matcher.tenant, + "/application/v4/tenant/{tenant}/application/"), - /** Paths used by application administrators. */ + /** Path for the base application resource. */ application(Matcher.tenant, Matcher.application, - "/application/v4/tenant/{tenant}/application/{application}", - "/application/v4/tenant/{tenant}/application/{application}/deploying/{*}", - "/application/v4/tenant/{tenant}/application/{application}/instance/{*}", - "/application/v4/tenant/{tenant}/application/{application}/environment/prod/region/{region}/instance/{instance}/global-rotation/override"),// TODO add restart path? - - /** Paths used for direct deployment to development zones. */ - development(Matcher.tenant, - Matcher.application, - "/application/v4/tenant/{tenant}/application/{application}/environment/dev/{*}", - "/application/v4/tenant/{tenant}/application/{application}/environment/perf/{*}"), + "/application/v4/tenant/{tenant}/application/{application}"), - /** Paths used for direct deployment to production zones. */ - deployment(Matcher.tenant, - Matcher.application, - "/application/v4/tenant/{tenant}/application/{application}/environment/prod/{*}", - "/application/v4/tenant/{tenant}/application/{application}/environment/test/{*}", - "/application/v4/tenant/{tenant}/application/{application}/environment/staging/{*}"), + /** Paths used by application administrators. */ + applicationInfo(Matcher.tenant, + Matcher.application, + "/application/v4/tenant/{tenant}/application/{application}/deploying/{*}", + "/application/v4/tenant/{tenant}/application/{application}/instance/{*}", + "/application/v4/tenant/{tenant}/application/{application}/environment/prod/region/{region}/instance/{instance}/logs", + "/application/v4/tenant/{tenant}/application/{application}/environment/prod/region/{region}/instance/{instance}/suspended", + "/application/v4/tenant/{tenant}/application/{application}/environment/prod/region/{region}/instance/{instance}/service/{*}", + "/application/v4/tenant/{tenant}/application/{application}/environment/prod/region/{region}/instance/{instance}/global-rotation/{*}"), + + /** Path used to restart application nodes. */ // TODO move to the above when everyone is on new pipeline. + applicationRestart(Matcher.tenant, + Matcher.application, + "/application/v4/tenant/{tenant}/application/{application}/environment/prod/region/{region}/instance/{ignored}/restart"), + + /** Paths used for development deployments. */ + developmentDeployment(Matcher.tenant, + Matcher.application, + "/application/v4/tenant/{tenant}/application/{application}/environment/dev/region/{region}/instance/{instance}", + "/application/v4/tenant/{tenant}/application/{application}/environment/dev/region/{region}/instance/{instance}/deploy", + "/application/v4/tenant/{tenant}/application/{application}/environment/perf/region/{region}/instance/{instance}", + "/application/v4/tenant/{tenant}/application/{application}/environment/perf/region/{region}/instance/{instance}/deploy"), + + /** Paths used for production deployments. */ + productionDeployment(Matcher.tenant, + Matcher.application, + "/application/v4/tenant/{tenant}/application/{application}/environment/prod/region/{region}/instance/{instance}", + "/application/v4/tenant/{tenant}/application/{application}/environment/prod/region/{region}/instance/{instance}/deploy", + "/application/v4/tenant/{tenant}/application/{application}/environment/test/region/{region}/instance/{instance}", + "/application/v4/tenant/{tenant}/application/{application}/environment/test/region/{region}/instance/{instance}/deploy", + "/application/v4/tenant/{tenant}/application/{application}/environment/staging/region/{region}/instance/{instance}", + "/application/v4/tenant/{tenant}/application/{application}/environment/staging/region/{region}/instance/{instance}/deploy"), /** Paths used for continuous deployment to production. */ submission(Matcher.tenant, - Matcher.application, - "/application/v4/tenant/{tenant}/application/{application}/submit"), + Matcher.application, + "/application/v4/tenant/{tenant}/application/{application}/submit"), /** Paths used for other tasks by build services. */ // TODO: This will vanish. buildService(Matcher.tenant, Matcher.application, "/application/v4/tenant/{tenant}/application/{application}/jobreport", - "/application/v4/tenant/{tenant}/application/{application}/promote"), + "/application/v4/tenant/{tenant}/application/{application}/promote", + "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/promote"), /** Paths which contain (not very strictly) classified information about, e.g., customers. */ classifiedInfo("/athenz/v1/{*}", diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Policy.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Policy.java index c22e7adf9d7..6f1ce77f5cd 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Policy.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Policy.java @@ -23,53 +23,63 @@ public enum Policy { .in(SystemName.Public)), /** Access to create a user tenant in select systems. */ - onboardUser(Privilege.grant(Action.update) - .on(PathGroup.onboardingUser) - .in(SystemName.main, SystemName.cd, SystemName.dev)), + userCreate(Privilege.grant(Action.update) + .on(PathGroup.user) + .in(SystemName.main, SystemName.cd, SystemName.dev)), - /** Access to create a user tenant in select systems. */ - onboardTenant(Privilege.grant(Action.create) - .on(PathGroup.onboarding) - .in(SystemName.main, SystemName.cd, SystemName.dev)), // TODO SystemName.all() + /** Access to create a tenant in select systems. */ + tenantCreate(Privilege.grant(Action.create) + .on(PathGroup.tenant) + .in(SystemName.main, SystemName.cd, SystemName.dev)), // TODO SystemName.all() /** Full access to tenant information and settings. */ - tenant(Privilege.grant(Action.all()) - .on(PathGroup.tenant) - .in(SystemName.all())), + tenantWrite(Privilege.grant(Action.write()) + .on(PathGroup.tenant) + .in(SystemName.all())), /** Read access to tenant information and settings. */ tenantRead(Privilege.grant(Action.read) - .on(PathGroup.tenant) + .on(PathGroup.tenant, PathGroup.tenantInfo) .in(SystemName.all())), - /** Full access to application information, settings and jobs. */ - application(Privilege.grant(Action.all()) - .on(PathGroup.application) - .in(SystemName.all())), - - /** Full access to application information, settings and jobs. */ - applicationModify(Privilege.grant(Action.update) + /** Access to create application under a certain tenant. */ + applicationCreate(Privilege.grant(Action.create) .on(PathGroup.application) .in(SystemName.all())), /** Read access to application information and settings. */ applicationRead(Privilege.grant(Action.read) - .on(PathGroup.application) + .on(PathGroup.application, PathGroup.applicationInfo) .in(SystemName.all())), + /** Read access to application information and settings. */ + applicationUpdate(Privilege.grant(Action.update) + .on(PathGroup.application, PathGroup.applicationInfo) + .in(SystemName.all())), + + /** Access to delete a certain application. */ + applicationDelete(Privilege.grant(Action.delete) + .on(PathGroup.application) + .in(SystemName.all())), + + /** Full access to application information and settings. */ + applicationOperations(Privilege.grant(Action.write()) + .on(PathGroup.applicationInfo, PathGroup.applicationRestart) + .in(SystemName.all())), + /** Full access to application development deployments. */ - development(Privilege.grant(Action.all()) - .on(PathGroup.development) - .in(SystemName.all())), + developmentDeployment(Privilege.grant(Action.all()) + .on(PathGroup.developmentDeployment) + .in(SystemName.all())), /** Full access to application production deployments. */ - production(Privilege.grant(Action.all()) - .on(PathGroup.deployment) - .in(SystemName.all())), + productionDeployment(Privilege.grant(Action.all()) + .on(PathGroup.productionDeployment) + .in(SystemName.all())), - /** Read access to allapplication deployments. */ + /** Read access to all application deployments. */ deploymentRead(Privilege.grant(Action.read) - .on(PathGroup.development, PathGroup.deployment) + .on(PathGroup.developmentDeployment, PathGroup.productionDeployment) .in(SystemName.all())), /** Full access to submissions for continuous deployment. */ @@ -78,9 +88,9 @@ public enum Policy { .in(SystemName.all())), /** Full access to the additional tasks needed for continuous deployment. */ - buildService(Privilege.grant(Action.all()) - .on(PathGroup.buildService) - .in(SystemName.all())), + deploymentPipeline(Privilege.grant(Action.all()) // TODO remove when everyone is on new pipeline. + .on(PathGroup.buildService, PathGroup.applicationRestart) + .in(SystemName.all())), /** Read access to all information in select systems. */ classifiedRead(Privilege.grant(Action.read) diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Role.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Role.java index 3e792d43253..a195c1924fe 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Role.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Role.java @@ -6,45 +6,67 @@ import java.util.Set; /** * This declares all tenant roles known to the controller. A role contains one or more {@link Policy}'s which decide - * what actions a member of a role can perform. + * what actions a member of a role can perform, and, optionally, a "lower ranking" role from which all policies are + * inherited. Read the list of roles from everyone to tenantAdmin, in order, to see what policies such a role includes. * * @author mpolden + * @author jonmv */ public enum Role { /** Deus ex machina. */ hostedOperator(Policy.operator), - /** Tenant administrator with full access to all child resources. */ - tenantAdmin(Policy.manager, - Policy.tenant, - Policy.application, - Policy.development), // TODO remove, as it is covered by applicationAdmin. - - /** Build and continuous delivery service. */ - tenantPipelineOperator(Policy.buildService, - Policy.submission, - Policy.production), - - /** Application administrator with full access to an already existing application. */ - applicationAdmin(Policy.tenantRead, - Policy.applicationModify, - Policy.development, - Policy.production), - - /** Application operator with read access to all information about an application. */ - applicationOperator(Policy.tenantRead, - Policy.applicationRead, - Policy.deploymentRead), - /** Build service which may submit new applications for continuous deployment. */ - buildService(Policy.submission), + buildService(Policy.submission, + Policy.applicationRead), - /** Base role which everyone is part of. */ + /** Base role which every user is part of. */ everyone(Policy.classifiedRead, Policy.publicRead, - Policy.onboardUser, - Policy.onboardTenant); + Policy.userCreate, + Policy.tenantCreate), + + /** Application reader which can see all information about an application, its tenant and deployments. */ + applicationReader(everyone, + Policy.tenantRead, + Policy.applicationRead, + Policy.deploymentRead), + + /** Application developer with access to deploy to development zones. */ + applicationDeveloper(applicationReader, + Policy.developmentDeployment), + + /** Application operator with access to normal, operational tasks of an application. */ + applicationOperator(applicationDeveloper, + Policy.applicationOperations), + + /** Application administrator with full access to an already existing application, including emergency operations. */ + applicationAdmin(applicationOperator, + Policy.applicationUpdate, + Policy.productionDeployment, + Policy.submission), + + /** Tenant admin with full access to all tenant resources, including the ability to create new applications. */ + tenantAdmin(applicationAdmin, + Policy.applicationCreate, + Policy.applicationDelete, + Policy.manager, + Policy.tenantWrite), + + /** Build and continuous delivery service. */ // TODO replace with buildService, when everyone is on new pipeline. + tenantPipeline(Policy.submission, + Policy.deploymentPipeline, + Policy.productionDeployment), + + /** Tenant administrator with full access to all child resources. */ + athenzTenantAdmin(Policy.tenantWrite, + Policy.tenantRead, + Policy.applicationCreate, + Policy.applicationUpdate, + Policy.applicationDelete, + Policy.applicationOperations, + Policy.developmentDeployment); // TODO remove, as it is covered by applicationAdmin. private final Set<Policy> policies; @@ -52,6 +74,11 @@ public enum Role { this.policies = EnumSet.copyOf(Set.of(policies)); } + Role(Role inherited, Policy... policies) { + this.policies = EnumSet.copyOf(Set.of(policies)); + this.policies.addAll(inherited.policies); + } + /** * Returns whether this role is allowed to perform action in given role context. Action is allowed if at least one * policy evaluates to true. diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleResolverTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleResolverTest.java index 5c86301d037..4628b95ad3c 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleResolverTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleResolverTest.java @@ -92,29 +92,29 @@ public class AthenzRoleResolverTest { // Operators and tenant admins are tenant admins of their tenants. assertEquals(Set.of(Context.limitedTo(TENANT, tester.controller().system())), - resolver.membership(HOSTED_OPERATOR, APPLICATION_CONTEXT_PATH).contextsFor(Role.tenantAdmin)); + resolver.membership(HOSTED_OPERATOR, APPLICATION_CONTEXT_PATH).contextsFor(Role.athenzTenantAdmin)); assertEquals(emptySet(), // TODO this is wrong, but we can't do better until we ask ZMS for roles. - resolver.membership(TENANT_ADMIN, NO_CONTEXT_PATH).contextsFor(Role.tenantAdmin)); + resolver.membership(TENANT_ADMIN, NO_CONTEXT_PATH).contextsFor(Role.athenzTenantAdmin)); assertEquals(Set.of(Context.limitedTo(TENANT, tester.controller().system())), - resolver.membership(TENANT_ADMIN, TENANT_CONTEXT_PATH).contextsFor(Role.tenantAdmin)); + resolver.membership(TENANT_ADMIN, TENANT_CONTEXT_PATH).contextsFor(Role.athenzTenantAdmin)); assertEquals(emptySet(), - resolver.membership(TENANT_ADMIN, TENANT2_CONTEXT_PATH).contextsFor(Role.tenantAdmin)); + resolver.membership(TENANT_ADMIN, TENANT2_CONTEXT_PATH).contextsFor(Role.athenzTenantAdmin)); assertEquals(emptySet(), - resolver.membership(TENANT_PIPELINE, APPLICATION_CONTEXT_PATH).contextsFor(Role.tenantAdmin)); + resolver.membership(TENANT_PIPELINE, APPLICATION_CONTEXT_PATH).contextsFor(Role.athenzTenantAdmin)); assertEquals(emptySet(), - resolver.membership(USER, TENANT_CONTEXT_PATH).contextsFor(Role.tenantAdmin)); + resolver.membership(USER, TENANT_CONTEXT_PATH).contextsFor(Role.athenzTenantAdmin)); // Only build services are pipeline operators of their applications. assertEquals(emptySet(), - resolver.membership(HOSTED_OPERATOR, APPLICATION_CONTEXT_PATH).contextsFor(Role.tenantPipelineOperator)); + resolver.membership(HOSTED_OPERATOR, APPLICATION_CONTEXT_PATH).contextsFor(Role.tenantPipeline)); assertEquals(emptySet(), - resolver.membership(TENANT_ADMIN, APPLICATION_CONTEXT_PATH).contextsFor(Role.tenantPipelineOperator)); + resolver.membership(TENANT_ADMIN, APPLICATION_CONTEXT_PATH).contextsFor(Role.tenantPipeline)); assertEquals(Set.of(Context.limitedTo(TENANT, APPLICATION, tester.controller().system())), - resolver.membership(TENANT_PIPELINE, APPLICATION_CONTEXT_PATH).contextsFor(Role.tenantPipelineOperator)); + resolver.membership(TENANT_PIPELINE, APPLICATION_CONTEXT_PATH).contextsFor(Role.tenantPipeline)); assertEquals(emptySet(), - resolver.membership(TENANT_PIPELINE, APPLICATION2_CONTEXT_PATH).contextsFor(Role.tenantPipelineOperator)); + resolver.membership(TENANT_PIPELINE, APPLICATION2_CONTEXT_PATH).contextsFor(Role.tenantPipeline)); assertEquals(emptySet(), - resolver.membership(USER, APPLICATION_CONTEXT_PATH).contextsFor(Role.tenantPipelineOperator)); + resolver.membership(USER, APPLICATION_CONTEXT_PATH).contextsFor(Role.tenantPipeline)); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/role/RoleMembershipTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/role/RoleMembershipTest.java index 2638e3f74e6..972ddefb1a5 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/role/RoleMembershipTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/role/RoleMembershipTest.java @@ -31,7 +31,7 @@ public class RoleMembershipTest { @Test public void tenant_membership() { RoleMembership roles = RoleMembership.in(SystemName.main) - .add(Role.tenantAdmin).limitedTo(TenantName.from("t1"), ApplicationName.from("a1")) + .add(Role.athenzTenantAdmin).limitedTo(TenantName.from("t1"), ApplicationName.from("a1")) .build(); assertFalse(roles.allows(Action.create, "/not/explicitly/defined")); assertFalse("Deny access to operator API", roles.allows(Action.create, "/controller/v1/foo")); @@ -40,15 +40,15 @@ public class RoleMembershipTest { assertTrue(roles.allows(Action.update, "/application/v4/tenant/t1/application/a1")); RoleMembership multiContext = RoleMembership.in(SystemName.main) - .add(Role.tenantAdmin).limitedTo(TenantName.from("t1"), ApplicationName.from("a1")) - .add(Role.tenantAdmin).limitedTo(TenantName.from("t2"), ApplicationName.from("a2")) + .add(Role.athenzTenantAdmin).limitedTo(TenantName.from("t1"), ApplicationName.from("a1")) + .add(Role.athenzTenantAdmin).limitedTo(TenantName.from("t2"), ApplicationName.from("a2")) .build(); assertFalse("Deny access to other tenant and app", multiContext.allows(Action.update, "/application/v4/tenant/t3/application/a3")); assertTrue(multiContext.allows(Action.update, "/application/v4/tenant/t2/application/a2")); assertTrue(multiContext.allows(Action.update, "/application/v4/tenant/t1/application/a1")); RoleMembership publicSystem = RoleMembership.in(SystemName.vaas) - .add(Role.tenantAdmin).limitedTo(TenantName.from("t1"), ApplicationName.from("a1")) + .add(Role.athenzTenantAdmin).limitedTo(TenantName.from("t1"), ApplicationName.from("a1")) .build(); assertFalse(publicSystem.allows(Action.read, "/controller/v1/foo")); assertTrue(multiContext.allows(Action.update, "/application/v4/tenant/t1/application/a1")); @@ -57,7 +57,7 @@ public class RoleMembershipTest { @Test public void build_service_membership() { RoleMembership roles = RoleMembership.in(SystemName.main) - .add(Role.tenantPipelineOperator).build(); + .add(Role.tenantPipeline).build(); assertFalse(roles.allows(Action.create, "/not/explicitly/defined")); assertFalse(roles.allows(Action.update, "/application/v4/tenant/t1/application/a1")); assertTrue(roles.allows(Action.create, "/application/v4/tenant/t1/application/a1/jobreport")); @@ -67,8 +67,8 @@ public class RoleMembershipTest { @Test public void multi_role_membership() { RoleMembership roles = RoleMembership.in(SystemName.main) - .add(Role.tenantAdmin).limitedTo(TenantName.from("t1"), ApplicationName.from("a1")) - .add(Role.tenantPipelineOperator) + .add(Role.athenzTenantAdmin).limitedTo(TenantName.from("t1"), ApplicationName.from("a1")) + .add(Role.tenantPipeline) .add(Role.everyone) .build(); assertFalse(roles.allows(Action.create, "/not/explicitly/defined")); |