aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authortoby <smorgrav@yahoo-inc.com>2020-02-12 11:14:15 +0100
committertoby <smorgrav@yahoo-inc.com>2020-02-12 11:14:15 +0100
commitd5bb58ac36d629e208b5234c56053f970bdcc384 (patch)
tree5ef3a4d99a4cb9b037808d06f6913214f81aa26b
parentbd386dd1642ffe2ef4cdb108f9f7c1a2c27b7ff9 (diff)
Add supporter role
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/Roles.java1
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Policy.java5
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Role.java5
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleDefinition.java3
-rw-r--r--controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/user/RolesTest.java2
-rw-r--r--controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleTest.java23
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilter.java3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilterTest.java6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java10
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()));