diff options
author | Jon Marius Venstad <venstad@gmail.com> | 2019-10-10 08:47:34 +0200 |
---|---|---|
committer | Jon Marius Venstad <venstad@gmail.com> | 2019-10-10 08:47:34 +0200 |
commit | 6d28d62eec52e7d3f5aec5c9cf10a14a1ae40e6d (patch) | |
tree | 916a4123c24c14e25020a3a22c3c1b4971686cec /controller-api | |
parent | 217cb2ee4ca2760388faa9c68be7fcf89422e87a (diff) |
Add InstanceRole and the instance role athenzUser
Diffstat (limited to 'controller-api')
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"); |