diff options
author | toby <smorgrav@yahoo-inc.com> | 2020-02-12 11:14:15 +0100 |
---|---|---|
committer | toby <smorgrav@yahoo-inc.com> | 2020-02-12 11:14:15 +0100 |
commit | d5bb58ac36d629e208b5234c56053f970bdcc384 (patch) | |
tree | 5ef3a4d99a4cb9b037808d06f6913214f81aa26b | |
parent | bd386dd1642ffe2ef4cdb108f9f7c1a2c27b7ff9 (diff) |
Add supporter role
10 files changed, 59 insertions, 3 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/Roles.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/Roles.java index 77bd589f23b..f5f3ebe8f35 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/Roles.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/Roles.java @@ -35,6 +35,7 @@ public class Roles { public static Role toRole(String value) { String[] parts = value.split("\\."); if (parts.length == 1 && parts[0].equals("hostedOperator")) return Role.hostedOperator(); + if (parts.length == 1 && parts[0].equals("hostedSupporter")) return Role.hostedSupporter(); if (parts.length == 2) return toRole(TenantName.from(parts[0]), parts[1]); if (parts.length == 3) return toRole(TenantName.from(parts[0]), ApplicationName.from(parts[1]), parts[2]); throw new IllegalArgumentException("Malformed or illegal role value '" + value + "'."); diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Policy.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Policy.java index e27fb0fbf27..0e8e3a13f9f 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Policy.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Policy.java @@ -23,6 +23,11 @@ enum Policy { .on(PathGroup.all()) .in(SystemName.all())), + /** Full access to everything. */ + supporter(Privilege.grant(Action.read) + .on(PathGroup.all()) + .in(SystemName.all())), + /** Full access to user management for a tenant in select systems. */ tenantManager(Privilege.grant(Action.all()) .on(PathGroup.tenantUsers) diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Role.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Role.java index b53cf9162e7..d852e55d7fd 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Role.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Role.java @@ -28,6 +28,11 @@ public abstract class Role { return new UnboundRole(RoleDefinition.hostedOperator); } + /** Returns a {@link RoleDefinition#hostedSupporter for the current system. */ + public static UnboundRole hostedSupporter() { + return new UnboundRole(RoleDefinition.hostedSupporter); + } + /** Returns a {@link RoleDefinition#everyone} for the current system. */ public static UnboundRole everyone() { return new UnboundRole(RoleDefinition.everyone); diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleDefinition.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleDefinition.java index 58d69512feb..848866f7c33 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleDefinition.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleDefinition.java @@ -21,6 +21,9 @@ public enum RoleDefinition { /** Deus ex machina. */ hostedOperator(Policy.operator), + /** Machina autem exspiravit. */ + hostedSupporter(Policy.supporter), + /** Base role which every user is part of. */ everyone(Policy.classifiedRead, Policy.classifiedApiRead, diff --git a/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/user/RolesTest.java b/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/user/RolesTest.java index cfb5462e50a..22baedd16b4 100644 --- a/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/user/RolesTest.java +++ b/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/user/RolesTest.java @@ -27,6 +27,8 @@ public class RolesTest { assertEquals(Role.hostedOperator(), Roles.toRole("hostedOperator")); + assertEquals(Role.hostedSupporter(), + Roles.toRole("hostedSupporter")); assertEquals(Role.tenantOperator(tenant), Roles.toRole("my-tenant.tenantOperator")); assertEquals(Role.applicationReader(tenant, application), diff --git a/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleTest.java b/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleTest.java index d153e218640..da2f64f2893 100644 --- a/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleTest.java +++ b/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleTest.java @@ -33,6 +33,27 @@ public class RoleTest { } @Test + public void supporter_membership() { + Role role = Role.hostedSupporter(); + + // No create update or delete + assertFalse(mainEnforcer.allows(role, Action.create, URI.create("/not/explicitly/defined"))); + assertFalse(mainEnforcer.allows(role, Action.create, URI.create("/controller/v1/foo"))); + assertFalse(mainEnforcer.allows(role, Action.update, URI.create("/os/v1/bar"))); + assertFalse(mainEnforcer.allows(role, Action.update, URI.create("/application/v4/tenant/t1/application/a1"))); + assertFalse(mainEnforcer.allows(role, Action.update, URI.create("/application/v4/tenant/t2/application/a2"))); + assertFalse(mainEnforcer.allows(role, Action.delete, URI.create("/application/v4/tenant/t8/application/a6/instance/i1/environment/dev/region/r1"))); + + // But reads is ok (but still only for valid paths) + assertFalse(mainEnforcer.allows(role, Action.read, URI.create("/not/explicitly/defined"))); + assertTrue(mainEnforcer.allows(role, Action.read, URI.create("/controller/v1/foo"))); + assertTrue(mainEnforcer.allows(role, Action.read, URI.create("/os/v1/bar"))); + assertTrue(mainEnforcer.allows(role, Action.read, URI.create("/application/v4/tenant/t1/application/a1"))); + assertTrue(mainEnforcer.allows(role, Action.read, URI.create("/application/v4/tenant/t2/application/a2"))); + assertFalse(mainEnforcer.allows(role, Action.delete, URI.create("/application/v4/tenant/t8/application/a6/instance/i1/environment/dev/region/r1"))); + } + + @Test public void tenant_membership() { Role role = Role.athenzTenantAdmin(TenantName.from("t1")); assertFalse(mainEnforcer.allows(role, Action.create, URI.create("/not/explicitly/defined"))); @@ -133,12 +154,14 @@ public class RoleTest { Action action = Action.update; assertTrue(mainEnforcer.allows(Role.systemFlagsDeployer(), action, deployUri)); assertTrue(mainEnforcer.allows(Role.hostedOperator(), action, deployUri)); + assertFalse(mainEnforcer.allows(Role.hostedSupporter(), action, deployUri)); assertFalse(mainEnforcer.allows(Role.systemFlagsDryrunner(), action, deployUri)); assertFalse(mainEnforcer.allows(Role.everyone(), action, deployUri)); URI dryrunUri = URI.create("/system-flags/v1/dryrun"); assertTrue(mainEnforcer.allows(Role.systemFlagsDeployer(), action, dryrunUri)); assertTrue(mainEnforcer.allows(Role.hostedOperator(), action, dryrunUri)); + assertFalse(mainEnforcer.allows(Role.hostedSupporter(), action, dryrunUri)); assertTrue(mainEnforcer.allows(Role.systemFlagsDryrunner(), action, dryrunUri)); assertFalse(mainEnforcer.allows(Role.everyone(), action, dryrunUri)); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java index 7ace62ab44d..628d7f48c85 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java @@ -215,6 +215,10 @@ public class AthenzFacade implements AccessControl { return hasAccess("modify", service.getDomain().getName() + ":hosted-vespa", identity); } + public boolean hasHostedSupporterAccess(AthenzIdentity identity) { + return hasAccess("read", service.getDomain().getName() + ":hosted-vespa", identity); + } + public boolean canLaunch(AthenzIdentity principal, AthenzService service) { return hasAccess("launch", service.getDomain().getName() + ":service."+service.getName(), principal); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilter.java index 1aaecb58a8d..ba974521278 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilter.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilter.java @@ -87,6 +87,9 @@ public class AthenzRoleFilter extends JsonSecurityRequestFilterBase { if (athenz.hasHostedOperatorAccess(identity)) roleMemberships.add(Role.hostedOperator()); + if (athenz.hasHostedSupporterAccess(identity)) + roleMemberships.add(Role.hostedSupporter()); + // Add all tenants that are accessible for this request athenz.accessibleTenants(tenants.asList(), new Credentials(principal)) .forEach(accessibleTenant -> roleMemberships.add(Role.athenzTenantAdmin(accessibleTenant.name()))); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilterTest.java index 82b97a5b144..bef27f7a2f5 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilterTest.java @@ -73,13 +73,13 @@ public class AthenzRoleFilterTest { public void testTranslations() { // Hosted operators are always members of the hostedOperator role. - assertEquals(Set.of(Role.hostedOperator(), Role.systemFlagsDeployer(), Role.systemFlagsDryrunner()), + assertEquals(Set.of(Role.hostedOperator(), Role.systemFlagsDeployer(), Role.systemFlagsDryrunner(), Role.hostedSupporter()), filter.roles(HOSTED_OPERATOR, NO_CONTEXT_PATH)); - assertEquals(Set.of(Role.hostedOperator(), Role.systemFlagsDeployer(), Role.systemFlagsDryrunner()), + assertEquals(Set.of(Role.hostedOperator(), Role.systemFlagsDeployer(), Role.systemFlagsDryrunner(), Role.hostedSupporter()), filter.roles(HOSTED_OPERATOR, TENANT_CONTEXT_PATH)); - assertEquals(Set.of(Role.hostedOperator(), Role.systemFlagsDeployer(), Role.systemFlagsDryrunner()), + assertEquals(Set.of(Role.hostedOperator(), Role.systemFlagsDeployer(), Role.systemFlagsDryrunner(), Role.hostedSupporter()), filter.roles(HOSTED_OPERATOR, APPLICATION_CONTEXT_PATH)); // Tenant admins are members of the athenzTenantAdmin role within their tenant subtree. diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java index a5520b42459..c95691fc120 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java @@ -43,6 +43,16 @@ public class ControllerAuthorizationFilterTest { } @Test + public void supporter() { + ControllerTester tester = new ControllerTester(); + SecurityContext securityContext = new SecurityContext(() -> "operator", Set.of(Role.hostedSupporter())); + ControllerAuthorizationFilter filter = createFilter(tester); + + assertIsForbidden(invokeFilter(filter, createRequest(Method.POST, "/zone/v2/path", securityContext))); + assertIsAllowed(invokeFilter(filter, createRequest(Method.GET, "/zone/v1/path", securityContext))); + } + + @Test public void unprivileged() { ControllerTester tester = new ControllerTester(); SecurityContext securityContext = new SecurityContext(() -> "user", Set.of(Role.everyone())); |