summaryrefslogtreecommitdiffstats
path: root/controller-api
diff options
context:
space:
mode:
authorJon Marius Venstad <venstad@gmail.com>2019-10-10 08:47:34 +0200
committerJon Marius Venstad <venstad@gmail.com>2019-10-10 08:47:34 +0200
commit6d28d62eec52e7d3f5aec5c9cf10a14a1ae40e6d (patch)
tree916a4123c24c14e25020a3a22c3c1b4971686cec /controller-api
parent217cb2ee4ca2760388faa9c68be7fcf89422e87a (diff)
Add InstanceRole and the instance role athenzUser
Diffstat (limited to 'controller-api')
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Context.java33
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/InstanceRole.java32
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java75
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Role.java6
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleDefinition.java4
-rw-r--r--controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleTest.java9
6 files changed, 118 insertions, 41 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Context.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Context.java
index 1ca36b75cf5..c6e361426ba 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Context.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Context.java
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller.api.role;
import com.yahoo.config.provision.ApplicationName;
+import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.TenantName;
@@ -17,10 +18,12 @@ class Context {
private final Optional<TenantName> tenant;
private final Optional<ApplicationName> application;
+ private final Optional<InstanceName> instance;
- private Context(Optional<TenantName> tenant, Optional<ApplicationName> application) {
+ private Context(Optional<TenantName> tenant, Optional<ApplicationName> application, Optional<InstanceName> instance) {
this.tenant = Objects.requireNonNull(tenant, "tenant must be non-null");
this.application = Objects.requireNonNull(application, "application must be non-null");
+ this.instance = Objects.requireNonNull(instance, "instance must be non-null");
}
/** A specific tenant this is valid for, if any */
@@ -33,19 +36,29 @@ class Context {
return application;
}
+ /** A specific instance this is valid for, if any */
+ Optional<InstanceName> instance() {
+ return instance;
+ }
+
/** Returns a context that has no restrictions on tenant or application */
static Context unlimited() {
- return new Context(Optional.empty(), Optional.empty());
+ return new Context(Optional.empty(), Optional.empty(), Optional.empty());
}
/** Returns a context that is limited to given tenant */
static Context limitedTo(TenantName tenant) {
- return new Context(Optional.of(tenant), Optional.empty());
+ return new Context(Optional.of(tenant), Optional.empty(), Optional.empty());
}
- /** Returns a context that is limited to given tenant, application */
+ /** Returns a context that is limited to given tenant and application */
static Context limitedTo(TenantName tenant, ApplicationName application) {
- return new Context(Optional.of(tenant), Optional.of(application));
+ return new Context(Optional.of(tenant), Optional.of(application), Optional.empty());
+ }
+
+ /** Returns a context that is limited to given tenant, application, and instance */
+ static Context limitedTo(TenantName tenant, ApplicationName application, InstanceName instance) {
+ return new Context(Optional.of(tenant), Optional.of(application), Optional.of(instance));
}
@Override
@@ -54,18 +67,20 @@ class Context {
if (o == null || getClass() != o.getClass()) return false;
Context context = (Context) o;
return tenant.equals(context.tenant) &&
- application.equals(context.application);
+ application.equals(context.application) &&
+ instance.equals(context.instance);
}
@Override
public int hashCode() {
- return Objects.hash(tenant, application);
+ return Objects.hash(tenant, application, instance);
}
@Override
public String toString() {
- return "tenant " + tenant.map(TenantName::value).orElse("[none]") + ", application " +
- application.map(ApplicationName::value).orElse("[none]");
+ return "tenant " + tenant.map(TenantName::value).orElse("[none]") + ", "
+ + "application " + application.map(ApplicationName::value).orElse("[none]") + ", "
+ + "instance " + instance.map(InstanceName::value).orElse("[none]");
}
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/InstanceRole.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/InstanceRole.java
new file mode 100644
index 00000000000..92f4e17017a
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/InstanceRole.java
@@ -0,0 +1,32 @@
+package com.yahoo.vespa.hosted.controller.api.role;
+
+import com.yahoo.config.provision.ApplicationName;
+import com.yahoo.config.provision.InstanceName;
+import com.yahoo.config.provision.TenantName;
+
+/**
+ * A {@link Role} with a {@link Context} of a {@link TenantName}, an {@link ApplicationName}, and an {@link InstanceName}.
+ *
+ * @author jonmv
+ */
+public class InstanceRole extends Role {
+
+ InstanceRole(RoleDefinition roleDefinition, TenantName tenant, ApplicationName application, InstanceName instance) {
+ super(roleDefinition, Context.limitedTo(tenant, application, instance));
+ }
+
+ /** Returns the {@link TenantName} this is bound to. */
+ public TenantName tenant() { return context.tenant().get(); }
+
+ /** Returns the {@link ApplicationName} this is bound to. */
+ public ApplicationName application() { return context.application().get(); }
+
+ /** Returns the {@link InstanceName} this is bound to. */
+ public InstanceName instance() { return context.instance().get(); }
+
+ @Override
+ public String toString() {
+ return "role '" + definition() + "' of instance '" + instance() + "' of '" + application() + "' owned by '" + tenant() + "'";
+ }
+
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java
index b0ec1a38824..31caa192bda 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java
@@ -63,7 +63,7 @@ enum PathGroup {
Optional.of("/api"),
"/application/v4/tenant/{tenant}/application/{application}",
"/application/v4/tenant/{tenant}/application/{application}/instance/",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}"),
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}"),
/** Paths used for user management on the application level. */
applicationUsers(Matcher.tenant,
@@ -76,28 +76,29 @@ enum PathGroup {
Matcher.application,
Optional.of("/api"),
"/application/v4/tenant/{tenant}/application/{application}/deploying/{*}",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/deploying/{*}",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/job/{*}",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/nodes",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/logs",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/suspended",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/service/{*}",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/{environment}/region/{region}/global-rotation/{*}",
- "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/nodes",
- "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/logs",
- "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/suspended",
- "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/service/{*}",
- "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/global-rotation/{*}",
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/deploying/{*}",
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/job/{*}",
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/{environment}/region/{region}/nodes",
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/{environment}/region/{region}/logs",
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/{environment}/region/{region}/suspended",
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/{environment}/region/{region}/service/{*}",
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/{environment}/region/{region}/global-rotation/{*}",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{ignored}/nodes",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{ignored}/logs",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{ignored}/suspended",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{ignored}/service/{*}",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{ignored}/global-rotation/{*}",
"/application/v4/tenant/{tenant}/application/{application}/metering"),
/** Path used to restart development nodes. */
developmentRestart(Matcher.tenant,
Matcher.application,
+ Matcher.instance,
Optional.of("/api"),
- "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/dev/region/{region}/restart",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/perf/region/{region}/restart",
- "/application/v4/tenant/{tenant}/application/{application}/environment/dev/region/{region}/instance/{ignored}/restart",
- "/application/v4/tenant/{tenant}/application/{application}/environment/perf/region/{region}/instance/{ignored}/restart"),
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/dev/region/{region}/restart",
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/perf/region/{region}/restart",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/dev/region/{region}/instance/{instance}/restart",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/perf/region/{region}/instance/{instance}/restart"),
/** Path used to restart production nodes. */
productionRestart(Matcher.tenant,
@@ -113,6 +114,7 @@ enum PathGroup {
/** Paths used for development deployments. */
developmentDeployment(Matcher.tenant,
Matcher.application,
+ Matcher.instance,
Optional.of("/api"),
"/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/deploy/{job}",
"/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/dev/region/{region}",
@@ -128,32 +130,32 @@ enum PathGroup {
productionDeployment(Matcher.tenant,
Matcher.application,
Optional.of("/api"),
- "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/prod/region/{region}",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/prod/region/{region}/deploy",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/test/region/{region}",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/test/region/{region}/deploy",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/staging/region/{region}",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/environment/staging/region/{region}/deploy",
- "/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"),
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/prod/region/{region}",
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/prod/region/{region}/deploy",
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/test/region/{region}",
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/test/region/{region}/deploy",
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/staging/region/{region}",
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/environment/staging/region/{region}/deploy",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/prod/region/{region}/instance/{ignored}",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/prod/region/{region}/instance/{ignored}/deploy",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/test/region/{region}/instance/{ignored}",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/test/region/{region}/instance/{ignored}/deploy",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/staging/region/{region}/instance/{ignored}",
+ "/application/v4/tenant/{tenant}/application/{application}/environment/staging/region/{region}/instance/{ignored}/deploy"),
/** Paths used for continuous deployment to production. */
submission(Matcher.tenant,
Matcher.application,
Optional.of("/api"),
"/application/v4/tenant/{tenant}/application/{application}/submit",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/submit"),
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/submit"),
/** Paths used for other tasks by build services. */ // TODO: This will vanish.
buildService(Matcher.tenant,
Matcher.application,
Optional.of("/api"),
"/application/v4/tenant/{tenant}/application/{application}/jobreport",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{instance}/jobreport"),
+ "/application/v4/tenant/{tenant}/application/{application}/instance/{ignored}/jobreport"),
/** Paths which contain (not very strictly) classified information about customers. */
classifiedTenantInfo(Optional.of("/api"),
@@ -201,6 +203,10 @@ enum PathGroup {
this(List.of(first, second), prefix, List.of(pathSpecs));
}
+ PathGroup(Matcher first, Matcher second, Matcher third, Optional<String> prefix, String... pathSpecs) {
+ this(List.of(first, second, third), prefix, List.of(pathSpecs));
+ }
+
/** Creates a new path group, if the given context matchers are each present exactly once in each of the given specs. */
PathGroup(List<Matcher> matchers, Optional<String> prefix, List<String> pathSpecs) {
this.matchers = matchers;
@@ -233,6 +239,10 @@ enum PathGroup {
if (application != null && context.application().isPresent()) {
match &= context.application().get().value().equals(application);
}
+ String instance = p.get(Matcher.instance.name);
+ if (instance != null && context.instance().isPresent()) {
+ match &= context.instance().get().value().equals(instance);
+ }
return match;
}).orElse(false);
}
@@ -242,7 +252,8 @@ enum PathGroup {
enum Matcher {
tenant("{tenant}"),
- application("{application}");
+ application("{application}"),
+ instance("{instance}");
final String pattern;
final String name;
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 606db8a0f2f..03f5a949c99 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
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller.api.role;
import com.yahoo.config.provision.ApplicationName;
+import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.TenantName;
import java.net.URI;
@@ -43,6 +44,11 @@ public abstract class Role {
return new ApplicationRole(RoleDefinition.tenantPipeline, tenant, application);
}
+ /** Returns a {@link RoleDefinition#athenzUser} for the current system and given tenant and application. */
+ public static InstanceRole athenzUser(TenantName tenant, ApplicationName application, InstanceName instance) {
+ return new InstanceRole(RoleDefinition.athenzUser, tenant, application, instance);
+ }
+
/** Returns a {@link RoleDefinition#tenantOwner} for the current system and given tenant. */
public static TenantRole tenantOwner(TenantName tenant) {
return new TenantRole(RoleDefinition.tenantOwner, tenant);
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 8e3754777ea..9797a3813ed 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
@@ -99,6 +99,10 @@ public enum RoleDefinition {
Policy.deploymentPipeline,
Policy.productionDeployment),
+ /** Athenz user with access to development resources under its instances. */
+ athenzUser(everyone,
+ Policy.developmentDeployment),
+
/** Tenant administrator with full access to all child resources. */
athenzTenantAdmin(everyone,
Policy.tenantRead,
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 e1248ab857f..fcac8c1efd2 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
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller.api.role;
import com.yahoo.config.provision.ApplicationName;
+import com.yahoo.config.provision.InstanceName;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.TenantName;
import org.junit.Test;
@@ -55,6 +56,14 @@ public class RoleTest {
}
@Test
+ public void athenz_user_membership() {
+ Role role = Role.athenzUser(TenantName.from("t8"), ApplicationName.from("a6"), InstanceName.from("i1"));
+ assertTrue(mainEnforcer.allows(role, Action.create, URI.create("/application/v4/tenant/t8/application/a6/instance/i1/deploy/some-job")));
+ assertTrue(mainEnforcer.allows(role, Action.delete, URI.create("/application/v4/tenant/t8/application/a6/instance/i1/environment/dev/region/r1")));
+ assertFalse(mainEnforcer.allows(role, Action.delete, URI.create("/application/v4/tenant/t8/application/a6/instance/i1/environment/prod/region/r1")));
+ }
+
+ @Test
public void implications() {
TenantName tenant1 = TenantName.from("t1");
ApplicationName application1 = ApplicationName.from("a1");