summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Marius Venstad <jonmv@users.noreply.github.com>2019-04-15 11:58:40 +0200
committerGitHub <noreply@github.com>2019-04-15 11:58:40 +0200
commitbabfc4859ef4586711eb150b87c02415132a274b (patch)
tree57ab49955df4a14b35192c47ac7b797dcaf423ab
parent62ae43a918e2ab66ddaaa4f3964cfa255e5aa307 (diff)
parent132c65ea92960f583ada0db4fcf5796d85bf6a4c (diff)
Merge pull request #9129 from vespa-engine/jvenstad/really-hide-system-in-role-API
Jvenstad/really hide system in role api
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/Roles.java (renamed from controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserRoles.java)59
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Action.java4
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/ApplicationRole.java6
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Context.java40
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Enforcer.java25
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java6
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Policy.java10
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Privilege.java22
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Role.java73
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Roles.java98
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/TenantRole.java6
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/UnboundRole.java6
-rw-r--r--controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/user/RolesTest.java65
-rw-r--r--controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserRolesTest.java72
-rw-r--r--controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleTest.java66
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java11
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilter.java11
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java11
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java21
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/CloudAccessControl.java21
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerCloudTest.java3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java1
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilterTest.java34
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java11
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java34
25 files changed, 323 insertions, 393 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserRoles.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/Roles.java
index 479fcbd2589..0eff7de3f9f 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserRoles.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/user/Roles.java
@@ -5,75 +5,60 @@ import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.hosted.controller.api.role.ApplicationRole;
import com.yahoo.vespa.hosted.controller.api.role.Role;
import com.yahoo.vespa.hosted.controller.api.role.RoleDefinition;
-import com.yahoo.vespa.hosted.controller.api.role.Roles;
import com.yahoo.vespa.hosted.controller.api.role.TenantRole;
import java.util.List;
-import static java.util.Objects.requireNonNull;
-
/**
* Validation, utility and serialization methods for roles used in user management.
*
* @author jonmv
*/
-public class UserRoles {
-
- private final Roles roles;
+public class Roles {
- /** Creates a new UserRoles which can be used for serialisation and listing of bound user roles. */
- public UserRoles(Roles roles) {
- this.roles = requireNonNull(roles);
- }
+ private Roles() { }
/** Returns the list of {@link TenantRole}s a {@link UserId} may be a member of. */
- public List<TenantRole> tenantRoles(TenantName tenant) {
- return List.of(roles.tenantOwner(tenant),
- roles.tenantAdmin(tenant),
- roles.tenantOperator(tenant));
+ public static List<TenantRole> tenantRoles(TenantName tenant) {
+ return List.of(Role.tenantOwner(tenant),
+ Role.tenantAdmin(tenant),
+ Role.tenantOperator(tenant));
}
/** Returns the list of {@link ApplicationRole}s a {@link UserId} may be a member of. */
- public List<ApplicationRole> applicationRoles(TenantName tenant, ApplicationName application) {
- return List.of(roles.applicationAdmin(tenant, application),
- roles.applicationOperator(tenant, application),
- roles.applicationDeveloper(tenant, application),
- roles.applicationReader(tenant, application));
+ public static List<ApplicationRole> applicationRoles(TenantName tenant, ApplicationName application) {
+ return List.of(Role.applicationAdmin(tenant, application),
+ Role.applicationOperator(tenant, application),
+ Role.applicationDeveloper(tenant, application),
+ Role.applicationReader(tenant, application));
}
/** Returns the {@link Role} the given value represents. */
- public Role toRole(String value) {
+ public static Role toRole(String value) {
String[] parts = value.split("\\.");
- if (parts.length == 1) return toOperatorRole(parts[0]);
+ if (parts.length == 1 && parts[0].equals("hostedOperator")) return Role.hostedOperator();
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 + "'.");
}
- public Role toOperatorRole(String roleName) {
- switch (roleName) {
- case "hostedOperator": return roles.hostedOperator();
- default: throw new IllegalArgumentException("Malformed or illegal role name '" + roleName + "'.");
- }
- }
-
/** Returns the {@link Role} the given tenant, application and role names correspond to. */
- public Role toRole(TenantName tenant, String roleName) {
+ public static Role toRole(TenantName tenant, String roleName) {
switch (roleName) {
- case "tenantOwner": return roles.tenantOwner(tenant);
- case "tenantAdmin": return roles.tenantAdmin(tenant);
- case "tenantOperator": return roles.tenantOperator(tenant);
+ case "tenantOwner": return Role.tenantOwner(tenant);
+ case "tenantAdmin": return Role.tenantAdmin(tenant);
+ case "tenantOperator": return Role.tenantOperator(tenant);
default: throw new IllegalArgumentException("Malformed or illegal role name '" + roleName + "'.");
}
}
/** Returns the {@link Role} the given tenant and role names correspond to. */
- public Role toRole(TenantName tenant, ApplicationName application, String roleName) {
+ public static Role toRole(TenantName tenant, ApplicationName application, String roleName) {
switch (roleName) {
- case "applicationAdmin": return roles.applicationAdmin(tenant, application);
- case "applicationOperator": return roles.applicationOperator(tenant, application);
- case "applicationDeveloper": return roles.applicationDeveloper(tenant, application);
- case "applicationReader": return roles.applicationReader(tenant, application);
+ case "applicationAdmin": return Role.applicationAdmin(tenant, application);
+ case "applicationOperator": return Role.applicationOperator(tenant, application);
+ case "applicationDeveloper": return Role.applicationDeveloper(tenant, application);
+ case "applicationReader": return Role.applicationReader(tenant, application);
default: throw new IllegalArgumentException("Malformed or illegal role name '" + roleName + "'.");
}
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Action.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Action.java
index 2d9ef25d1f5..0aa6cfd1f84 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Action.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Action.java
@@ -18,11 +18,11 @@ public enum Action {
update,
delete;
- public static EnumSet<Action> all() {
+ static EnumSet<Action> all() {
return EnumSet.allOf(Action.class);
}
- public static EnumSet<Action> write() {
+ static EnumSet<Action> write() {
return EnumSet.of(create, update, delete);
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/ApplicationRole.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/ApplicationRole.java
index cc1e8462580..37ef06d461b 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/ApplicationRole.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/ApplicationRole.java
@@ -5,14 +5,14 @@ import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.TenantName;
/**
- * A {@link Role} with a {@link Context} of a {@link SystemName} a {@link TenantName} and an {@link ApplicationName}.
+ * A {@link Role} with a {@link Context} of a {@link TenantName} and an {@link ApplicationName}.
*
* @author jonmv
*/
public class ApplicationRole extends Role {
- ApplicationRole(RoleDefinition roleDefinition, SystemName system, TenantName tenant, ApplicationName application) {
- super(roleDefinition, Context.limitedTo(tenant, application, system));
+ ApplicationRole(RoleDefinition roleDefinition, TenantName tenant, ApplicationName application) {
+ super(roleDefinition, Context.limitedTo(tenant, application));
}
/** Returns the {@link TenantName} this is bound to. */
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 14d8d06d0c6..1ca36b75cf5 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
@@ -13,46 +13,39 @@ import java.util.Optional;
*
* @author mpolden
*/
-public class Context {
+class Context {
private final Optional<TenantName> tenant;
private final Optional<ApplicationName> application;
- private final SystemName system;
- private Context(Optional<TenantName> tenant, Optional<ApplicationName> application, SystemName system) {
+ private Context(Optional<TenantName> tenant, Optional<ApplicationName> application) {
this.tenant = Objects.requireNonNull(tenant, "tenant must be non-null");
this.application = Objects.requireNonNull(application, "application must be non-null");
- this.system = Objects.requireNonNull(system, "system must be non-null");
}
/** A specific tenant this is valid for, if any */
- public Optional<TenantName> tenant() {
+ Optional<TenantName> tenant() {
return tenant;
}
/** A specific application this is valid for, if any */
- public Optional<ApplicationName> application() {
+ Optional<ApplicationName> application() {
return application;
}
- /** System in which this is valid */
- public SystemName system() {
- return system;
+ /** Returns a context that has no restrictions on tenant or application */
+ static Context unlimited() {
+ return new Context(Optional.empty(), Optional.empty());
}
- /** Returns a context that has no restrictions on tenant or application in given system */
- public static Context unlimitedIn(SystemName system) {
- return new Context(Optional.empty(), Optional.empty(), system);
+ /** Returns a context that is limited to given tenant */
+ static Context limitedTo(TenantName tenant) {
+ return new Context(Optional.of(tenant), Optional.empty());
}
- /** Returns a context that is limited to given tenant and system */
- public static Context limitedTo(TenantName tenant, SystemName system) {
- return new Context(Optional.of(tenant), Optional.empty(), system);
- }
-
- /** Returns a context that is limited to given tenant, application and system */
- public static Context limitedTo(TenantName tenant, ApplicationName application, SystemName system) {
- return new Context(Optional.of(tenant), Optional.of(application), system);
+ /** Returns a context that is limited to given tenant, application */
+ static Context limitedTo(TenantName tenant, ApplicationName application) {
+ return new Context(Optional.of(tenant), Optional.of(application));
}
@Override
@@ -61,19 +54,18 @@ public class Context {
if (o == null || getClass() != o.getClass()) return false;
Context context = (Context) o;
return tenant.equals(context.tenant) &&
- application.equals(context.application) &&
- system == context.system;
+ application.equals(context.application);
}
@Override
public int hashCode() {
- return Objects.hash(tenant, application, system);
+ return Objects.hash(tenant, application);
}
@Override
public String toString() {
return "tenant " + tenant.map(TenantName::value).orElse("[none]") + ", application " +
- application.map(ApplicationName::value).orElse("[none]") + ", system " + system;
+ application.map(ApplicationName::value).orElse("[none]");
}
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Enforcer.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Enforcer.java
new file mode 100644
index 00000000000..c83e25c6073
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Enforcer.java
@@ -0,0 +1,25 @@
+package com.yahoo.vespa.hosted.controller.api.role;
+
+import com.yahoo.config.provision.SystemName;
+
+import java.net.URI;
+
+/**
+ * Checks whether {@link Role}s have the required {@link Privilege}s to perform {@link Action}s on given {@link java.net.URI}s.
+ *
+ * @author jonmv
+ */
+public class Enforcer {
+
+ private final SystemName system;
+
+ public Enforcer(SystemName system) {
+ this.system = system;
+ }
+
+ /** Returns whether {@code role} has permission to perform {@code action} on {@code resource}, in this enforcer's system. */
+ public boolean allows(Role role, Action action, URI resource) {
+ return role.definition().policies().stream().anyMatch(policy -> policy.evaluate(action, resource, role.context, system));
+ }
+
+}
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 23bf8514b9c..c64d1f9f89b 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
@@ -17,7 +17,7 @@ import java.util.Set;
* @author mpolden
* @author jonmv
*/
-public enum PathGroup {
+enum PathGroup {
/** Paths used for system management by operators. */
operator("/controller/v1/{*}",
@@ -170,12 +170,12 @@ public enum PathGroup {
}
/** All known path groups */
- public static Set<PathGroup> all() {
+ static Set<PathGroup> all() {
return EnumSet.allOf(PathGroup.class);
}
/** Returns whether this group matches path in given context */
- public boolean matches(URI uri, Context context) {
+ boolean matches(URI uri, Context context) {
return get(uri).map(p -> {
boolean match = true;
String tenant = p.get(Matcher.tenant.name);
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 db4cca20b9a..15745d69dc5 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
@@ -11,12 +11,12 @@ import java.util.Set;
/**
* Policies for REST APIs in the controller. A policy is only considered when defined in a {@link Role}.
* A policy describes a set of {@link Privilege}s, which are valid for a set of {@link SystemName}s.
- * A policy is evaluated with a {@link Context}, which provides the {@link SystemName} the policy is
- * evaluated in, and any limitations to a specific {@link TenantName} or {@link ApplicationName}.
+ * A policy is evaluated by an {@link Enforcer}, which holds the {@link SystemName} the evaluation is done in.
+ * A policy is evaluated with a {@link Context}, which may limit it to a specific {@link TenantName} or {@link ApplicationName}.
*
* @author mpolden
*/
-public enum Policy {
+enum Policy {
/** Full access to everything. */
operator(Privilege.grant(Action.all())
@@ -125,9 +125,9 @@ public enum Policy {
}
/** Returns whether action is allowed on path in given context */
- public boolean evaluate(Action action, URI uri, Context context) {
+ boolean evaluate(Action action, URI uri, Context context, SystemName system) {
return privileges.stream().anyMatch(privilege -> privilege.actions().contains(action) &&
- privilege.systems().contains(context.system()) &&
+ privilege.systems().contains(system) &&
privilege.pathGroups().stream()
.anyMatch(pg -> pg.matches(uri, context)));
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Privilege.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Privilege.java
index a53717b25d6..896b22a8676 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Privilege.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Privilege.java
@@ -14,7 +14,7 @@ import java.util.Set;
*
* @author mpolden
*/
-public class Privilege {
+class Privilege {
private final Set<SystemName> systems;
private final Set<Action> actions;
@@ -30,17 +30,17 @@ public class Privilege {
}
/** Systems where this applies */
- public Set<SystemName> systems() {
+ Set<SystemName> systems() {
return systems;
}
/** Actions allowed by this */
- public Set<Action> actions() {
+ Set<Action> actions() {
return actions;
}
/** Path groups where this applies */
- public Set<PathGroup> pathGroups() {
+ Set<PathGroup> pathGroups() {
return pathGroups;
}
@@ -59,15 +59,15 @@ public class Privilege {
return Objects.hash(systems, actions, pathGroups);
}
- public static PrivilegeBuilder grant(Action... actions) {
+ static PrivilegeBuilder grant(Action... actions) {
return grant(Set.of(actions));
}
- public static PrivilegeBuilder grant(Set<Action> actions) {
+ static PrivilegeBuilder grant(Set<Action> actions) {
return new PrivilegeBuilder(actions);
}
- public static class PrivilegeBuilder {
+ static class PrivilegeBuilder {
private Set<Action> actions;
private Set<PathGroup> pathGroups;
@@ -77,20 +77,20 @@ public class Privilege {
this.pathGroups = new LinkedHashSet<>();
}
- public PrivilegeBuilder on(PathGroup... pathGroups) {
+ PrivilegeBuilder on(PathGroup... pathGroups) {
return on(Set.of(pathGroups));
}
- public PrivilegeBuilder on(Set<PathGroup> pathGroups) {
+ PrivilegeBuilder on(Set<PathGroup> pathGroups) {
this.pathGroups.addAll(pathGroups);
return this;
}
- public Privilege in(SystemName... systems) {
+ Privilege in(SystemName... systems) {
return in(Set.of(systems));
}
- public Privilege in(Set<SystemName> systems) {
+ Privilege in(Set<SystemName> systems) {
return new Privilege(systems, actions, pathGroups);
}
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 c28fa7a3fc3..f36107db228 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
@@ -1,12 +1,15 @@
// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.api.role;
+import com.yahoo.config.provision.ApplicationName;
+import com.yahoo.config.provision.TenantName;
+
import java.net.URI;
import java.util.Objects;
/**
* A role is a combination of a {@link RoleDefinition} and a {@link Context}, which allows evaluation
- * of access control for a given action on a resource. Create using {@link Roles}.
+ * of access control for a given action on a resource.
*
* @author jonmv
*/
@@ -20,19 +23,71 @@ public abstract class Role {
this.context = Objects.requireNonNull(context);
}
- /** Returns the role definition of this bound role. */
- public RoleDefinition definition() { return roleDefinition; }
+ /** Returns a {@link RoleDefinition#hostedOperator} for the current system. */
+ public static UnboundRole hostedOperator() {
+ return new UnboundRole(RoleDefinition.hostedOperator);
+ }
+
+ /** Returns a {@link RoleDefinition#everyone} for the current system. */
+ public static UnboundRole everyone() {
+ return new UnboundRole(RoleDefinition.everyone);
+ }
+
+ /** Returns a {@link RoleDefinition#athenzTenantAdmin} for the current system and given tenant. */
+ public static TenantRole athenzTenantAdmin(TenantName tenant) {
+ return new TenantRole(RoleDefinition.athenzTenantAdmin, tenant);
+ }
+
+ /** Returns a {@link RoleDefinition#tenantPipeline} for the current system and given tenant and application. */
+ public static ApplicationRole tenantPipeline(TenantName tenant, ApplicationName application) {
+ return new ApplicationRole(RoleDefinition.tenantPipeline, tenant, application);
+ }
+
+ /** Returns a {@link RoleDefinition#tenantOwner} for the current system and given tenant. */
+ public static TenantRole tenantOwner(TenantName tenant) {
+ return new TenantRole(RoleDefinition.tenantOwner, tenant);
+ }
+
+ /** Returns a {@link RoleDefinition#tenantAdmin} for the current system and given tenant. */
+ public static TenantRole tenantAdmin(TenantName tenant) {
+ return new TenantRole(RoleDefinition.tenantAdmin, tenant);
+ }
- /** Returns whether this role is allowed to perform the given action on the given resource. */
- public final boolean allows(Action action, URI uri) {
- return roleDefinition.policies().stream().anyMatch(policy -> policy.evaluate(action, uri, context));
+ /** Returns a {@link RoleDefinition#tenantOperator} for the current system and given tenant. */
+ public static TenantRole tenantOperator(TenantName tenant) {
+ return new TenantRole(RoleDefinition.tenantOperator, tenant);
}
+ /** Returns a {@link RoleDefinition#applicationAdmin} for the current system and given tenant and application. */
+ public static ApplicationRole applicationAdmin(TenantName tenant, ApplicationName application) {
+ return new ApplicationRole(RoleDefinition.applicationAdmin, tenant, application);
+ }
+
+ /** Returns a {@link RoleDefinition#applicationOperator} for the current system and given tenant and application. */
+ public static ApplicationRole applicationOperator(TenantName tenant, ApplicationName application) {
+ return new ApplicationRole(RoleDefinition.applicationOperator, tenant, application);
+ }
+
+ /** Returns a {@link RoleDefinition#applicationDeveloper} for the current system and given tenant and application. */
+ public static ApplicationRole applicationDeveloper(TenantName tenant, ApplicationName application) {
+ return new ApplicationRole(RoleDefinition.applicationDeveloper, tenant, application);
+ }
+
+ /** Returns a {@link RoleDefinition#applicationReader} for the current system and given tenant and application. */
+ public static ApplicationRole applicationReader(TenantName tenant, ApplicationName application) {
+ return new ApplicationRole(RoleDefinition.applicationReader, tenant, application);
+ }
+
+ /** Returns a {@link RoleDefinition#buildService} for the current system and given tenant and application. */
+ public static ApplicationRole buildService(TenantName tenant, ApplicationName application) {
+ return new ApplicationRole(RoleDefinition.buildService, tenant, application);
+ }
+
+ /** Returns the role definition of this bound role. */
+ public RoleDefinition definition() { return roleDefinition; }
+
/** Returns whether the other role is a parent of this, and has a context included in this role's context. */
public boolean implies(Role other) {
- if ( ! context.system().equals(other.context.system()))
- throw new IllegalStateException("Coexisting roles should always be in the same system.");
-
return (context.tenant().isEmpty() || context.tenant().equals(other.context.tenant()))
&& (context.application().isEmpty() || context.application().equals(other.context.application()))
&& roleDefinition.inherited().contains(other.roleDefinition);
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Roles.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Roles.java
deleted file mode 100644
index bfdb0be4378..00000000000
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Roles.java
+++ /dev/null
@@ -1,98 +0,0 @@
-package com.yahoo.vespa.hosted.controller.api.role;
-
-import com.google.inject.Inject;
-import com.yahoo.config.provision.ApplicationName;
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.config.provision.TenantName;
-import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry;
-
-import java.util.Objects;
-
-/**
- * Use if you need to create {@link Role}s for its system.
- *
- * This also defines the relationship between {@link RoleDefinition}s and their required {@link Context}s.
- *
- * @author jonmv
- */
-public class Roles {
-
- private final SystemName system;
-
- @Inject
- public Roles(ZoneRegistry zones) {
- this(zones.system());
- }
-
- /** Creates a Roles which can be used to create bound roles for the given system. */
- public Roles(SystemName system) {
- this.system = Objects.requireNonNull(system);
- }
-
-
- // General roles.
- /** Returns a {@link RoleDefinition#hostedOperator} for the current system. */
- public UnboundRole hostedOperator() {
- return new UnboundRole(RoleDefinition.hostedOperator, system);
- }
-
- /** Returns a {@link RoleDefinition#everyone} for the current system. */
- public UnboundRole everyone() {
- return new UnboundRole(RoleDefinition.everyone, system);
- }
-
-
- // Athenz based roles.
- /** Returns a {@link RoleDefinition#athenzTenantAdmin} for the current system and given tenant. */
- public TenantRole athenzTenantAdmin(TenantName tenant) {
- return new TenantRole(RoleDefinition.athenzTenantAdmin, system, tenant);
- }
-
- /** Returns a {@link RoleDefinition#tenantPipeline} for the current system and given tenant and application. */
- public ApplicationRole tenantPipeline(TenantName tenant, ApplicationName application) {
- return new ApplicationRole(RoleDefinition.tenantPipeline, system, tenant, application);
- }
-
-
- // Other identity provider based roles.
- /** Returns a {@link RoleDefinition#tenantOwner} for the current system and given tenant. */
- public TenantRole tenantOwner(TenantName tenant) {
- return new TenantRole(RoleDefinition.tenantOwner, system, tenant);
- }
-
- /** Returns a {@link RoleDefinition#tenantAdmin} for the current system and given tenant. */
- public TenantRole tenantAdmin(TenantName tenant) {
- return new TenantRole(RoleDefinition.tenantAdmin, system, tenant);
- }
-
- /** Returns a {@link RoleDefinition#tenantOperator} for the current system and given tenant. */
- public TenantRole tenantOperator(TenantName tenant) {
- return new TenantRole(RoleDefinition.tenantOperator, system, tenant);
- }
-
- /** Returns a {@link RoleDefinition#applicationAdmin} for the current system and given tenant and application. */
- public ApplicationRole applicationAdmin(TenantName tenant, ApplicationName application) {
- return new ApplicationRole(RoleDefinition.applicationAdmin, system, tenant, application);
- }
-
- /** Returns a {@link RoleDefinition#applicationOperator} for the current system and given tenant and application. */
- public ApplicationRole applicationOperator(TenantName tenant, ApplicationName application) {
- return new ApplicationRole(RoleDefinition.applicationOperator, system, tenant, application);
- }
-
- /** Returns a {@link RoleDefinition#applicationDeveloper} for the current system and given tenant and application. */
- public ApplicationRole applicationDeveloper(TenantName tenant, ApplicationName application) {
- return new ApplicationRole(RoleDefinition.applicationDeveloper, system, tenant, application);
- }
-
- /** Returns a {@link RoleDefinition#applicationReader} for the current system and given tenant and application. */
- public ApplicationRole applicationReader(TenantName tenant, ApplicationName application) {
- return new ApplicationRole(RoleDefinition.applicationReader, system, tenant, application);
- }
-
- /** Returns a {@link RoleDefinition#buildService} for the current system and given tenant and application. */
- public ApplicationRole buildService(TenantName tenant, ApplicationName application) {
- return new ApplicationRole(RoleDefinition.buildService, system, tenant, application);
- }
-
-}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/TenantRole.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/TenantRole.java
index 134628ec3a3..8712b436c0d 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/TenantRole.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/TenantRole.java
@@ -4,14 +4,14 @@ import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.TenantName;
/**
- * A {@link Role} with a {@link Context} of a {@link SystemName} and a {@link TenantName}.
+ * A {@link Role} with a {@link Context} of a {@link TenantName}.
*
* @author jonmv
*/
public class TenantRole extends Role {
- TenantRole(RoleDefinition roleDefinition, SystemName system, TenantName tenant) {
- super(roleDefinition, Context.limitedTo(tenant, system));
+ TenantRole(RoleDefinition roleDefinition, TenantName tenant) {
+ super(roleDefinition, Context.limitedTo(tenant));
}
/** Returns the {@link TenantName} this is bound to. */
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/UnboundRole.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/UnboundRole.java
index eb8319b2012..93774b25210 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/UnboundRole.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/UnboundRole.java
@@ -3,14 +3,14 @@ package com.yahoo.vespa.hosted.controller.api.role;
import com.yahoo.config.provision.SystemName;
/**
- * A {@link Role} with a {@link Context} of only a {@link SystemName}.
+ * A {@link Role} with an unlimited {@link Context}.
*
* @author jonmv
*/
public class UnboundRole extends Role {
- UnboundRole(RoleDefinition roleDefinition, SystemName system) {
- super(roleDefinition, Context.unlimitedIn(system));
+ UnboundRole(RoleDefinition roleDefinition) {
+ super(roleDefinition, Context.unlimited());
}
@Override
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
new file mode 100644
index 00000000000..4c7fe57a6d8
--- /dev/null
+++ b/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/user/RolesTest.java
@@ -0,0 +1,65 @@
+package com.yahoo.vespa.hosted.controller.api.integration.user;
+
+import com.yahoo.config.provision.ApplicationName;
+import com.yahoo.config.provision.TenantName;
+import com.yahoo.vespa.hosted.controller.api.role.ApplicationRole;
+import com.yahoo.vespa.hosted.controller.api.role.Role;
+import com.yahoo.vespa.hosted.controller.api.role.TenantRole;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author jonmv
+ */
+public class RolesTest {
+
+ @Test
+ public void testSerialization() {
+ TenantName tenant = TenantName.from("my-tenant");
+ for (TenantRole role : Roles.tenantRoles(tenant))
+ assertEquals(role, Roles.toRole(Roles.valueOf(role)));
+
+ ApplicationName application = ApplicationName.from("my-application");
+ for (ApplicationRole role : Roles.applicationRoles(tenant, application))
+ assertEquals(role, Roles.toRole(Roles.valueOf(role)));
+
+ assertEquals(Role.hostedOperator(),
+ Roles.toRole("hostedOperator"));
+ assertEquals(Role.tenantOperator(tenant),
+ Roles.toRole("my-tenant.tenantOperator"));
+ assertEquals(Role.applicationReader(tenant, application),
+ Roles.toRole("my-tenant.my-application.applicationReader"));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void illegalTenantName() {
+ Roles.valueOf(Role.tenantAdmin(TenantName.from("my.tenant")));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void illegalApplicationName() {
+ Roles.valueOf(Role.applicationOperator(TenantName.from("my-tenant"), ApplicationName.from("my.app")));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void illegalRole() {
+ Roles.valueOf(Role.tenantPipeline(TenantName.from("my-tenant"), ApplicationName.from("my-app")));
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void illegalRoleValue() {
+ Roles.toRole("my-tenant.awesomePerson");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void illegalCombination() {
+ Roles.toRole("my-tenant.my-application.tenantOwner");
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void illegalValue() {
+ Roles.toRole("everyone");
+ }
+
+}
diff --git a/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserRolesTest.java b/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserRolesTest.java
deleted file mode 100644
index 89df7a24559..00000000000
--- a/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/integration/user/UserRolesTest.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package com.yahoo.vespa.hosted.controller.api.integration.user;
-
-import com.yahoo.config.provision.ApplicationName;
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.config.provision.TenantName;
-import com.yahoo.vespa.hosted.controller.api.role.ApplicationRole;
-import com.yahoo.vespa.hosted.controller.api.role.Roles;
-import com.yahoo.vespa.hosted.controller.api.role.TenantRole;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-
-/**
- * @author jonmv
- */
-public class UserRolesTest {
-
- private static final Roles roles = new Roles(SystemName.main);
- private static final UserRoles userRoles = new UserRoles(roles);
-
- @Test
- public void testSerialization() {
- TenantName tenant = TenantName.from("my-tenant");
- for (TenantRole role : userRoles.tenantRoles(tenant))
- assertEquals(role, userRoles.toRole(UserRoles.valueOf(role)));
-
- ApplicationName application = ApplicationName.from("my-application");
- for (ApplicationRole role : userRoles.applicationRoles(tenant, application))
- assertEquals(role, userRoles.toRole(UserRoles.valueOf(role)));
-
- assertEquals(roles.tenantOperator(tenant),
- userRoles.toRole("my-tenant.tenantOperator"));
- assertEquals(roles.applicationReader(tenant, application),
- userRoles.toRole("my-tenant.my-application.applicationReader"));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void illegalTenantName() {
- UserRoles.valueOf(roles.tenantAdmin(TenantName.from("my.tenant")));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void illegalApplicationName() {
- UserRoles.valueOf(roles.applicationOperator(TenantName.from("my-tenant"), ApplicationName.from("my.app")));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void illegalRole() {
- UserRoles.valueOf(roles.tenantPipeline(TenantName.from("my-tenant"), ApplicationName.from("my-app")));
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void illegalRoleValue() {
- userRoles.toRole("my-tenant.awesomePerson");
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void illegalCombination() {
- userRoles.toRole("my-tenant.my-application.tenantOwner");
- }
-
- @Test(expected = IllegalArgumentException.class)
- public void illegalValue() {
- userRoles.toRole("everyone");
- }
-
- @Test
- public void allowHostedOperator() {
- assertEquals(roles.hostedOperator(), userRoles.toRole("hostedOperator"));
- }
-
-}
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 6cfe01cfb77..4c11da3b697 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
@@ -16,59 +16,61 @@ import static org.junit.Assert.assertTrue;
*/
public class RoleTest {
+ private static final Enforcer mainEnforcer = new Enforcer(SystemName.main);
+ private static final Enforcer vaasEnforcer = new Enforcer(SystemName.vaas);
+
@Test
public void operator_membership() {
- Role role = new Roles(SystemName.main).hostedOperator();
+ Role role = Role.hostedOperator();
// Operator actions
- assertFalse(role.allows(Action.create, URI.create("/not/explicitly/defined")));
- assertTrue(role.allows(Action.create, URI.create("/controller/v1/foo")));
- assertTrue(role.allows(Action.update, URI.create("/os/v1/bar")));
- assertTrue(role.allows(Action.update, URI.create("/application/v4/tenant/t1/application/a1")));
- assertTrue(role.allows(Action.update, URI.create("/application/v4/tenant/t2/application/a2")));
+ assertFalse(mainEnforcer.allows(role, Action.create, URI.create("/not/explicitly/defined")));
+ assertTrue(mainEnforcer.allows(role, Action.create, URI.create("/controller/v1/foo")));
+ assertTrue(mainEnforcer.allows(role, Action.update, URI.create("/os/v1/bar")));
+ assertTrue(mainEnforcer.allows(role, Action.update, URI.create("/application/v4/tenant/t1/application/a1")));
+ assertTrue(mainEnforcer.allows(role, Action.update, URI.create("/application/v4/tenant/t2/application/a2")));
}
@Test
public void tenant_membership() {
- Role role = new Roles(SystemName.main).athenzTenantAdmin(TenantName.from("t1"));
- assertFalse(role.allows(Action.create, URI.create("/not/explicitly/defined")));
- assertFalse("Deny access to operator API", role.allows(Action.create, URI.create("/controller/v1/foo")));
- assertFalse("Deny access to other tenant and app", role.allows(Action.update, URI.create("/application/v4/tenant/t2/application/a2")));
- assertTrue(role.allows(Action.update, URI.create("/application/v4/tenant/t1/application/a1")));
-
- Role publicSystem = new Roles(SystemName.vaas).athenzTenantAdmin(TenantName.from("t1"));
- assertFalse(publicSystem.allows(Action.read, URI.create("/controller/v1/foo")));
- assertTrue(publicSystem.allows(Action.read, URI.create("/badge/v1/badge")));
- assertTrue(publicSystem.allows(Action.update, URI.create("/application/v4/tenant/t1/application/a1")));
+ Role role = Role.athenzTenantAdmin(TenantName.from("t1"));
+ assertFalse(mainEnforcer.allows(role, Action.create, URI.create("/not/explicitly/defined")));
+ assertFalse("Deny access to operator API", mainEnforcer.allows(role, Action.create, URI.create("/controller/v1/foo")));
+ assertFalse("Deny access to other tenant and app", mainEnforcer.allows(role, Action.update, URI.create("/application/v4/tenant/t2/application/a2")));
+ assertTrue(mainEnforcer.allows(role, Action.update, URI.create("/application/v4/tenant/t1/application/a1")));
+
+ Role publicSystem = Role.athenzTenantAdmin(TenantName.from("t1"));
+ assertFalse(vaasEnforcer.allows(publicSystem, Action.read, URI.create("/controller/v1/foo")));
+ assertTrue(vaasEnforcer.allows(publicSystem, Action.read, URI.create("/badge/v1/badge")));
+ assertTrue(vaasEnforcer.allows(publicSystem, Action.update, URI.create("/application/v4/tenant/t1/application/a1")));
}
@Test
public void build_service_membership() {
- Role role = new Roles(SystemName.vaas).tenantPipeline(TenantName.from("t1"), ApplicationName.from("a1"));
- assertFalse(role.allows(Action.create, URI.create("/not/explicitly/defined")));
- assertFalse(role.allows(Action.update, URI.create("/application/v4/tenant/t1/application/a1")));
- assertTrue(role.allows(Action.create, URI.create("/application/v4/tenant/t1/application/a1/jobreport")));
- assertFalse("No global read access", role.allows(Action.read, URI.create("/controller/v1/foo")));
+ Role role = Role.tenantPipeline(TenantName.from("t1"), ApplicationName.from("a1"));
+ assertFalse(vaasEnforcer.allows(role, Action.create, URI.create("/not/explicitly/defined")));
+ assertFalse(vaasEnforcer.allows(role, Action.update, URI.create("/application/v4/tenant/t1/application/a1")));
+ assertTrue(vaasEnforcer.allows(role, Action.create, URI.create("/application/v4/tenant/t1/application/a1/jobreport")));
+ assertFalse("No global read access", vaasEnforcer.allows(role, Action.read, URI.create("/controller/v1/foo")));
}
@Test
public void implications() {
- Roles roles = new Roles(SystemName.main);
TenantName tenant1 = TenantName.from("t1");
ApplicationName application1 = ApplicationName.from("a1");
TenantName tenant2 = TenantName.from("t2");
ApplicationName application2 = ApplicationName.from("a2");
- Role tenantOwner1 = roles.tenantOwner(tenant1);
- Role tenantAdmin1 = roles.tenantAdmin(tenant1);
- Role tenantAdmin2 = roles.tenantAdmin(tenant2);
- Role tenantOperator1 = roles.tenantOperator(tenant1);
- Role applicationAdmin11 = roles.applicationAdmin(tenant1, application1);
- Role applicationOperator11 = roles.applicationOperator(tenant1, application1);
- Role applicationDeveloper11 = roles.applicationDeveloper(tenant1, application1);
- Role applicationReader11 = roles.applicationReader(tenant1, application1);
- Role applicationReader12 = roles.applicationReader(tenant1, application2);
- Role applicationReader22 = roles.applicationReader(tenant2, application2);
+ Role tenantOwner1 = Role.tenantOwner(tenant1);
+ Role tenantAdmin1 = Role.tenantAdmin(tenant1);
+ Role tenantAdmin2 = Role.tenantAdmin(tenant2);
+ Role tenantOperator1 = Role.tenantOperator(tenant1);
+ Role applicationAdmin11 = Role.applicationAdmin(tenant1, application1);
+ Role applicationOperator11 = Role.applicationOperator(tenant1, application1);
+ Role applicationDeveloper11 = Role.applicationDeveloper(tenant1, application1);
+ Role applicationReader11 = Role.applicationReader(tenant1, application1);
+ Role applicationReader12 = Role.applicationReader(tenant1, application2);
+ Role applicationReader22 = Role.applicationReader(tenant2, application2);
assertFalse(tenantOwner1.implies(tenantOwner1));
assertTrue(tenantOwner1.implies(tenantAdmin1));
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java
index 9886e5c1329..365b7960958 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java
@@ -23,11 +23,10 @@ import com.yahoo.vespa.hosted.controller.api.integration.github.GitHub;
import com.yahoo.vespa.hosted.controller.api.integration.organization.Mailer;
import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingGenerator;
import com.yahoo.config.provision.zone.ZoneId;
-import com.yahoo.vespa.hosted.controller.api.integration.user.UserRoles;
+import com.yahoo.vespa.hosted.controller.api.integration.user.Roles;
import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry;
import com.yahoo.vespa.hosted.controller.api.role.ApplicationRole;
import com.yahoo.vespa.hosted.controller.api.role.Role;
-import com.yahoo.vespa.hosted.controller.api.role.Roles;
import com.yahoo.vespa.hosted.controller.api.role.TenantRole;
import com.yahoo.vespa.hosted.controller.auditlog.AuditLogger;
import com.yahoo.vespa.hosted.controller.deployment.JobController;
@@ -82,7 +81,6 @@ public class Controller extends AbstractComponent {
private final Mailer mailer;
private final AuditLogger auditLogger;
private final FlagSource flagSource;
- private final UserRoles roles;
/**
* Creates a controller
@@ -135,7 +133,6 @@ public class Controller extends AbstractComponent {
);
tenantController = new TenantController(this, curator, accessControl);
auditLogger = new AuditLogger(curator, clock);
- roles = new UserRoles(new Roles(zoneRegistry.system()));
// Record the version of this controller
curator().writeControllerVersion(this.hostname(), Vtag.currentVersion);
@@ -298,16 +295,16 @@ public class Controller extends AbstractComponent {
/** Returns all other roles the given tenant role implies. */
public Set<Role> impliedRoles(TenantRole role) {
- return Stream.concat(roles.tenantRoles(role.tenant()).stream(),
+ return Stream.concat(Roles.tenantRoles(role.tenant()).stream(),
applications().asList(role.tenant()).stream()
- .flatMap(application -> roles.applicationRoles(application.id().tenant(), application.id().application()).stream()))
+ .flatMap(application -> Roles.applicationRoles(application.id().tenant(), application.id().application()).stream()))
.filter(role::implies)
.collect(Collectors.toUnmodifiableSet());
}
/** Returns all other roles the given application role implies. */
public Set<Role> impliedRoles(ApplicationRole role) {
- return roles.applicationRoles(role.tenant(), role.application()).stream()
+ return Roles.applicationRoles(role.tenant(), role.application()).stream()
.filter(role::implies)
.collect(Collectors.toUnmodifiableSet());
}
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 8e397366203..15cdf034ca0 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
@@ -17,7 +17,6 @@ import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.TenantController;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactory;
import com.yahoo.vespa.hosted.controller.api.role.Role;
-import com.yahoo.vespa.hosted.controller.api.role.Roles;
import com.yahoo.vespa.hosted.controller.athenz.ApplicationAction;
import com.yahoo.vespa.hosted.controller.athenz.impl.AthenzFacade;
import com.yahoo.vespa.hosted.controller.api.role.SecurityContext;
@@ -44,14 +43,12 @@ public class AthenzRoleFilter extends CorsRequestFilterBase { // TODO: No need f
private final AthenzFacade athenz;
private final TenantController tenants;
- private final Roles roles;
@Inject
public AthenzRoleFilter(CorsFilterConfig config, AthenzClientFactory athenzClientFactory, Controller controller) {
super(Set.copyOf(config.allowedUrls()));
this.athenz = new AthenzFacade(athenzClientFactory);
this.tenants = controller.tenants();
- this.roles = new Roles(controller.system());
}
@Override
@@ -80,18 +77,18 @@ public class AthenzRoleFilter extends CorsRequestFilterBase { // TODO: No need f
AthenzIdentity identity = principal.getIdentity();
if (athenz.hasHostedOperatorAccess(identity))
- return Set.of(roles.hostedOperator());
+ return Set.of(Role.hostedOperator());
if (tenant.isPresent() && isTenantAdmin(identity, tenant.get()))
- return Set.of(roles.athenzTenantAdmin(tenant.get().name()));
+ return Set.of(Role.athenzTenantAdmin(tenant.get().name()));
if (identity.getDomain().equals(SCREWDRIVER_DOMAIN) && application.isPresent() && tenant.isPresent())
// NOTE: Only fine-grained deploy authorization for Athenz tenants
if ( tenant.get().type() != Tenant.Type.athenz
|| hasDeployerAccess(identity, ((AthenzTenant) tenant.get()).domain(), application.get()))
- return Set.of(roles.tenantPipeline(tenant.get().name(), application.get()));
+ return Set.of(Role.tenantPipeline(tenant.get().name(), application.get()));
- return Set.of(roles.everyone());
+ return Set.of(Role.everyone());
}
private boolean isTenantAdmin(AthenzIdentity identity, Tenant tenant) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java
index 181731ef896..d07ad1ca907 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java
@@ -11,11 +11,10 @@ import com.yahoo.jdisc.http.filter.security.cors.CorsRequestFilterBase;
import com.yahoo.log.LogLevel;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.api.role.Action;
+import com.yahoo.vespa.hosted.controller.api.role.Enforcer;
import com.yahoo.vespa.hosted.controller.api.role.Role;
-import com.yahoo.vespa.hosted.controller.api.role.Roles;
import com.yahoo.vespa.hosted.controller.api.role.SecurityContext;
-import java.security.Principal;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Logger;
@@ -29,7 +28,7 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase {
private static final Logger log = Logger.getLogger(ControllerAuthorizationFilter.class.getName());
- private final Roles roles;
+ private final Enforcer enforcer;
@Inject
public ControllerAuthorizationFilter(Controller controller,
@@ -40,7 +39,7 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase {
ControllerAuthorizationFilter(SystemName system,
Set<String> allowedUrls) {
super(allowedUrls);
- this.roles = new Roles(system);
+ this.enforcer = new Enforcer(system);
}
@Override
@@ -54,11 +53,11 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase {
Action action = Action.from(HttpRequest.Method.valueOf(request.getMethod()));
// Avoid expensive look-ups when request is always legal.
- if (roles.everyone().allows(action, request.getUri()))
+ if (enforcer.allows(Role.everyone(), action, request.getUri()))
return Optional.empty();
Set<Role> roles = securityContext.get().roles();
- if (roles.stream().anyMatch(role -> role.allows(action, request.getUri())))
+ if (roles.stream().anyMatch(role -> enforcer.allows(role, action, request.getUri())))
return Optional.empty();
}
catch (Exception e) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java
index b8c904a80f6..5ef997b6d55 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiHandler.java
@@ -15,10 +15,9 @@ import com.yahoo.slime.Slime;
import com.yahoo.vespa.config.SlimeUtils;
import com.yahoo.vespa.hosted.controller.api.integration.user.UserId;
import com.yahoo.vespa.hosted.controller.api.integration.user.UserManagement;
-import com.yahoo.vespa.hosted.controller.api.integration.user.UserRoles;
+import com.yahoo.vespa.hosted.controller.api.integration.user.Roles;
import com.yahoo.vespa.hosted.controller.api.role.Role;
import com.yahoo.vespa.hosted.controller.api.role.RoleDefinition;
-import com.yahoo.vespa.hosted.controller.api.role.Roles;
import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse;
import com.yahoo.vespa.hosted.controller.restapi.MessageResponse;
import com.yahoo.vespa.hosted.controller.restapi.SlimeJsonResponse;
@@ -46,13 +45,11 @@ public class UserApiHandler extends LoggingRequestHandler {
private final static Logger log = Logger.getLogger(UserApiHandler.class.getName());
private static final String optionalPrefix = "/api";
- private final UserRoles roles;
private final UserManagement users;
@Inject
- public UserApiHandler(Context parentCtx, Roles roles, UserManagement users) {
+ public UserApiHandler(Context parentCtx, UserManagement users) {
super(parentCtx);
- this.roles = new UserRoles(roles);
this.users = users;
}
@@ -112,7 +109,7 @@ public class UserApiHandler extends LoggingRequestHandler {
Cursor root = slime.setObject();
root.setString("tenant", tenantName);
fillRoles(root,
- roles.tenantRoles(TenantName.from(tenantName)),
+ Roles.tenantRoles(TenantName.from(tenantName)),
Collections.emptyList());
return new SlimeJsonResponse(slime);
}
@@ -123,8 +120,8 @@ public class UserApiHandler extends LoggingRequestHandler {
root.setString("tenant", tenantName);
root.setString("application", applicationName);
fillRoles(root,
- roles.applicationRoles(TenantName.from(tenantName), ApplicationName.from(applicationName)),
- roles.tenantRoles(TenantName.from(tenantName)));
+ Roles.applicationRoles(TenantName.from(tenantName), ApplicationName.from(applicationName)),
+ Roles.tenantRoles(TenantName.from(tenantName)));
return new SlimeJsonResponse(slime);
}
@@ -159,7 +156,7 @@ public class UserApiHandler extends LoggingRequestHandler {
Inspector requestObject = bodyInspector(request);
String roleName = require("roleName", Inspector::asString, requestObject);
UserId user = new UserId(require("user", Inspector::asString, requestObject));
- Role role = roles.toRole(TenantName.from(tenantName), roleName);
+ Role role = Roles.toRole(TenantName.from(tenantName), roleName);
users.addUsers(role, List.of(user));
return new MessageResponse(user + " is now a member of " + role);
}
@@ -168,7 +165,7 @@ public class UserApiHandler extends LoggingRequestHandler {
Inspector requestObject = bodyInspector(request);
String roleName = require("roleName", Inspector::asString, requestObject);
UserId user = new UserId(require("user", Inspector::asString, requestObject));
- Role role = roles.toRole(TenantName.from(tenantName), ApplicationName.from(applicationName), roleName);
+ Role role = Roles.toRole(TenantName.from(tenantName), ApplicationName.from(applicationName), roleName);
users.addUsers(role, List.of(user));
return new MessageResponse(user + " is now a member of " + role);
}
@@ -177,7 +174,7 @@ public class UserApiHandler extends LoggingRequestHandler {
Inspector requestObject = bodyInspector(request);
String roleName = require("roleName", Inspector::asString, requestObject);
UserId user = new UserId(require("user", Inspector::asString, requestObject));
- Role role = roles.toRole(TenantName.from(tenantName), roleName);
+ Role role = Roles.toRole(TenantName.from(tenantName), roleName);
if ( role.definition() == RoleDefinition.tenantOwner
&& users.listUsers(role).equals(List.of(user)))
throw new IllegalArgumentException("Can't remove the last owner of a tenant.");
@@ -190,7 +187,7 @@ public class UserApiHandler extends LoggingRequestHandler {
Inspector requestObject = bodyInspector(request);
String roleName = require("roleName", Inspector::asString, requestObject);
UserId user = new UserId(require("user", Inspector::asString, requestObject));
- Role role = roles.toRole(TenantName.from(tenantName), ApplicationName.from(applicationName), roleName);
+ Role role = Roles.toRole(TenantName.from(tenantName), ApplicationName.from(applicationName), roleName);
users.removeUsers(role, List.of(user));
return new MessageResponse(user + " is no longer a member of " + role);
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/CloudAccessControl.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/CloudAccessControl.java
index 008be2fd276..f803ab9f29c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/CloudAccessControl.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/CloudAccessControl.java
@@ -7,10 +7,9 @@ import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.api.integration.organization.Marketplace;
import com.yahoo.vespa.hosted.controller.api.integration.user.UserId;
import com.yahoo.vespa.hosted.controller.api.integration.user.UserManagement;
-import com.yahoo.vespa.hosted.controller.api.integration.user.UserRoles;
+import com.yahoo.vespa.hosted.controller.api.integration.user.Roles;
import com.yahoo.vespa.hosted.controller.api.role.ApplicationRole;
import com.yahoo.vespa.hosted.controller.api.role.Role;
-import com.yahoo.vespa.hosted.controller.api.role.Roles;
import com.yahoo.vespa.hosted.controller.api.role.TenantRole;
import com.yahoo.vespa.hosted.controller.tenant.CloudTenant;
import com.yahoo.vespa.hosted.controller.tenant.Tenant;
@@ -25,15 +24,11 @@ public class CloudAccessControl implements AccessControl {
private final Marketplace marketplace;
private final UserManagement userManagement;
- private final Roles roles;
- private final UserRoles userRoles;
@Inject
- public CloudAccessControl(Marketplace marketplace, UserManagement userManagement, Roles roles) {
+ public CloudAccessControl(Marketplace marketplace, UserManagement userManagement) {
this.marketplace = marketplace;
this.userManagement = userManagement;
- this.roles = roles;
- this.userRoles = new UserRoles(roles);
}
@Override
@@ -41,9 +36,9 @@ public class CloudAccessControl implements AccessControl {
CloudTenantSpec spec = (CloudTenantSpec) tenantSpec;
CloudTenant tenant = new CloudTenant(spec.tenant(), marketplace.resolveCustomer(spec.getRegistrationToken()));
- for (Role role : userRoles.tenantRoles(spec.tenant()))
+ for (Role role : Roles.tenantRoles(spec.tenant()))
userManagement.createRole(role);
- userManagement.addUsers(roles.tenantOwner(spec.tenant()), List.of(new UserId(credentials.user().getName())));
+ userManagement.addUsers(Role.tenantOwner(spec.tenant()), List.of(new UserId(credentials.user().getName())));
return tenant;
}
@@ -57,20 +52,20 @@ public class CloudAccessControl implements AccessControl {
public void deleteTenant(TenantName tenant, Credentials credentials) {
// Probably terminate customer subscription?
- for (TenantRole role : userRoles.tenantRoles(tenant))
+ for (TenantRole role : Roles.tenantRoles(tenant))
userManagement.deleteRole(role);
}
@Override
public void createApplication(ApplicationId id, Credentials credentials) {
- for (Role role : userRoles.applicationRoles(id.tenant(), id.application()))
+ for (Role role : Roles.applicationRoles(id.tenant(), id.application()))
userManagement.createRole(role);
- userManagement.addUsers(roles.applicationAdmin(id.tenant(), id.application()), List.of(new UserId(credentials.user().getName())));
+ userManagement.addUsers(Role.applicationAdmin(id.tenant(), id.application()), List.of(new UserId(credentials.user().getName())));
}
@Override
public void deleteApplication(ApplicationId id, Credentials credentials) {
- for (ApplicationRole role : userRoles.applicationRoles(id.tenant(), id.application()))
+ for (ApplicationRole role : Roles.applicationRoles(id.tenant(), id.application()))
userManagement.deleteRole(role);
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerCloudTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerCloudTest.java
index 95477758deb..4f068451d24 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerCloudTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerCloudTest.java
@@ -3,7 +3,6 @@ package com.yahoo.vespa.hosted.controller.restapi;
import com.yahoo.application.container.handler.Request;
import com.yahoo.config.provision.SystemName;
import com.yahoo.vespa.hosted.controller.api.role.Role;
-import com.yahoo.vespa.hosted.controller.api.role.Roles;
import com.yahoo.vespa.hosted.controller.api.role.SecurityContext;
import java.nio.charset.StandardCharsets;
@@ -64,7 +63,7 @@ public class ControllerContainerCloudTest extends ControllerContainerTest {
private final Request.Method method;
private byte[] data = new byte[0];
private Principal user = () -> "user@test";
- private Set<Role> roles = Set.of(new Roles(system()).everyone());
+ private Set<Role> roles = Set.of(Role.everyone());
private RequestBuilder(String path, Request.Method method) {
this.path = path;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java
index 6abfa7fa72d..4cb0d509531 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java
@@ -96,7 +96,6 @@ public class ControllerContainerTest {
" <component id='com.yahoo.vespa.hosted.controller.integration.ApplicationStoreMock'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.api.integration.stubs.MockTesterCloud'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.api.integration.stubs.MockMailer'/>\n" +
- " <component id='com.yahoo.vespa.hosted.controller.api.role.Roles'/>\n" +
" <handler id='com.yahoo.vespa.hosted.controller.restapi.deployment.DeploymentApiHandler'>\n" +
" <binding>http://*/deployment/v1/*</binding>\n" +
" </handler>\n" +
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 b48cb4bff50..e36a02f387c 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
@@ -10,7 +10,7 @@ import com.yahoo.vespa.athenz.api.AthenzUser;
import com.yahoo.vespa.hosted.controller.ControllerTester;
import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId;
import com.yahoo.vespa.hosted.controller.api.identifiers.ScrewdriverId;
-import com.yahoo.vespa.hosted.controller.api.role.Roles;
+import com.yahoo.vespa.hosted.controller.api.role.Role;
import com.yahoo.vespa.hosted.controller.athenz.ApplicationAction;
import com.yahoo.vespa.hosted.controller.athenz.HostedAthenzIdentities;
import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzClientFactoryMock;
@@ -66,55 +66,53 @@ public class AthenzRoleFilterTest {
@Test
public void testTranslations() {
- Roles roles = new Roles(tester.controller().system());
-
// Hosted operators are always members of the hostedOperator role.
- assertEquals(Set.of(roles.hostedOperator()),
+ assertEquals(Set.of(Role.hostedOperator()),
filter.roles(HOSTED_OPERATOR, NO_CONTEXT_PATH));
- assertEquals(Set.of(roles.hostedOperator()),
+ assertEquals(Set.of(Role.hostedOperator()),
filter.roles(HOSTED_OPERATOR, TENANT_CONTEXT_PATH));
- assertEquals(Set.of(roles.hostedOperator()),
+ assertEquals(Set.of(Role.hostedOperator()),
filter.roles(HOSTED_OPERATOR, APPLICATION_CONTEXT_PATH));
// Tenant admins are members of the athenzTenantAdmin role within their tenant subtree.
- assertEquals(Set.of(roles.everyone()),
+ assertEquals(Set.of(Role.everyone()),
filter.roles(TENANT_PIPELINE, NO_CONTEXT_PATH));
- assertEquals(Set.of(roles.athenzTenantAdmin(TENANT)),
+ assertEquals(Set.of(Role.athenzTenantAdmin(TENANT)),
filter.roles(TENANT_ADMIN, TENANT_CONTEXT_PATH));
- assertEquals(Set.of(roles.athenzTenantAdmin(TENANT)),
+ assertEquals(Set.of(Role.athenzTenantAdmin(TENANT)),
filter.roles(TENANT_ADMIN, APPLICATION_CONTEXT_PATH));
- assertEquals(Set.of(roles.everyone()),
+ assertEquals(Set.of(Role.everyone()),
filter.roles(TENANT_ADMIN, TENANT2_CONTEXT_PATH));
- assertEquals(Set.of(roles.everyone()),
+ assertEquals(Set.of(Role.everyone()),
filter.roles(TENANT_ADMIN, APPLICATION2_CONTEXT_PATH));
// Build services are members of the tenantPipeline role within their application subtree.
- assertEquals(Set.of(roles.everyone()),
+ assertEquals(Set.of(Role.everyone()),
filter.roles(TENANT_PIPELINE, NO_CONTEXT_PATH));
- assertEquals(Set.of(roles.everyone()),
+ assertEquals(Set.of(Role.everyone()),
filter.roles(TENANT_PIPELINE, TENANT_CONTEXT_PATH));
- assertEquals(Set.of(roles.tenantPipeline(TENANT, APPLICATION)),
+ assertEquals(Set.of(Role.tenantPipeline(TENANT, APPLICATION)),
filter.roles(TENANT_PIPELINE, APPLICATION_CONTEXT_PATH));
- assertEquals(Set.of(roles.everyone()),
+ assertEquals(Set.of(Role.everyone()),
filter.roles(TENANT_PIPELINE, APPLICATION2_CONTEXT_PATH));
// Unprivileged users are just members of the everyone role.
- assertEquals(Set.of(roles.everyone()),
+ assertEquals(Set.of(Role.everyone()),
filter.roles(USER, NO_CONTEXT_PATH));
- assertEquals(Set.of(roles.everyone()),
+ assertEquals(Set.of(Role.everyone()),
filter.roles(USER, TENANT_CONTEXT_PATH));
- assertEquals(Set.of(roles.everyone()),
+ assertEquals(Set.of(Role.everyone()),
filter.roles(USER, APPLICATION_CONTEXT_PATH));
}
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 105e10eefd2..f2b0039750e 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
@@ -7,7 +7,7 @@ import com.yahoo.config.provision.SystemName;
import com.yahoo.jdisc.http.HttpRequest.Method;
import com.yahoo.jdisc.http.filter.DiscFilterRequest;
import com.yahoo.vespa.hosted.controller.ControllerTester;
-import com.yahoo.vespa.hosted.controller.api.role.Roles;
+import com.yahoo.vespa.hosted.controller.api.role.Role;
import com.yahoo.vespa.hosted.controller.api.role.SecurityContext;
import com.yahoo.vespa.hosted.controller.restapi.ApplicationRequestToDiscFilterRequestWrapper;
import org.junit.Test;
@@ -34,8 +34,7 @@ public class ControllerAuthorizationFilterTest {
@Test
public void operator() {
ControllerTester tester = new ControllerTester();
- Roles roles = new Roles(tester.controller().system());
- SecurityContext securityContext = new SecurityContext(() -> "operator", Set.of(roles.hostedOperator()));
+ SecurityContext securityContext = new SecurityContext(() -> "operator", Set.of(Role.hostedOperator()));
ControllerAuthorizationFilter filter = createFilter(tester);
assertIsAllowed(invokeFilter(filter, createRequest(Method.POST, "/zone/v2/path", securityContext)));
@@ -46,8 +45,7 @@ public class ControllerAuthorizationFilterTest {
@Test
public void unprivileged() {
ControllerTester tester = new ControllerTester();
- Roles roles = new Roles(tester.controller().system());
- SecurityContext securityContext = new SecurityContext(() -> "user", Set.of(roles.everyone()));
+ SecurityContext securityContext = new SecurityContext(() -> "user", Set.of(Role.everyone()));
ControllerAuthorizationFilter filter = createFilter(tester);
assertIsForbidden(invokeFilter(filter, createRequest(Method.POST, "/zone/v2/path", securityContext)));
@@ -59,8 +57,7 @@ public class ControllerAuthorizationFilterTest {
public void unprivilegedInPublic() {
ControllerTester tester = new ControllerTester();
tester.zoneRegistry().setSystemName(SystemName.Public);
- Roles roles = new Roles(tester.controller().system());
- SecurityContext securityContext = new SecurityContext(() -> "user", Set.of(roles.everyone()));
+ SecurityContext securityContext = new SecurityContext(() -> "user", Set.of(Role.everyone()));
ControllerAuthorizationFilter filter = createFilter(tester);
assertIsForbidden(invokeFilter(filter, createRequest(Method.POST, "/zone/v2/path", securityContext)));
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java
index 3a78e9fc262..59f63f0472a 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/user/UserApiTest.java
@@ -4,7 +4,6 @@ import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.SystemName;
import com.yahoo.config.provision.TenantName;
import com.yahoo.vespa.hosted.controller.api.role.Role;
-import com.yahoo.vespa.hosted.controller.api.role.Roles;
import com.yahoo.vespa.hosted.controller.restapi.ContainerTester;
import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerCloudTest;
import org.junit.Test;
@@ -28,8 +27,7 @@ public class UserApiTest extends ControllerContainerCloudTest {
public void testUserManagement() {
ContainerTester tester = new ContainerTester(container, responseFiles);
assertEquals(SystemName.Public, tester.controller().system());
- Roles roles = new Roles(tester.controller().system());
- Set<Role> operator = Set.of(roles.hostedOperator());
+ Set<Role> operator = Set.of(Role.hostedOperator());
ApplicationId id = ApplicationId.from("my-tenant", "my-app", "default");
@@ -70,80 +68,80 @@ public class UserApiTest extends ControllerContainerCloudTest {
// POST a hosted operator role is not allowed.
tester.assertResponse(request("/user/v1/tenant/my-tenant", POST)
- .roles(Set.of(roles.tenantOwner(id.tenant())))
+ .roles(Set.of(Role.tenantOwner(id.tenant())))
.data("{\"user\":\"evil@evil\",\"roleName\":\"hostedOperator\"}"),
"{\"error-code\":\"BAD_REQUEST\",\"message\":\"Malformed or illegal role name 'hostedOperator'.\"}", 400);
// POST a tenant operator is available to the tenant owner.
tester.assertResponse(request("/user/v1/tenant/my-tenant", POST)
- .roles(Set.of(roles.tenantOwner(id.tenant())))
+ .roles(Set.of(Role.tenantOwner(id.tenant())))
.data("{\"user\":\"operator@tenant\",\"roleName\":\"tenantOperator\"}"),
"{\"message\":\"user 'operator@tenant' is now a member of role 'tenantOperator' of 'my-tenant'\"}");
// POST a tenant admin is not available to a tenant operator.
tester.assertResponse(request("/user/v1/tenant/my-tenant", POST)
- .roles(Set.of(roles.tenantOperator(id.tenant())))
+ .roles(Set.of(Role.tenantOperator(id.tenant())))
.data("{\"user\":\"admin@tenant\",\"roleName\":\"tenantAdmin\"}"),
accessDenied, 403);
// POST an application admin for a non-existent application fails.
tester.assertResponse(request("/user/v1/tenant/my-tenant/application/my-app", POST)
- .roles(Set.of(roles.tenantOwner(TenantName.from("my-tenant"))))
+ .roles(Set.of(Role.tenantOwner(TenantName.from("my-tenant"))))
.data("{\"user\":\"admin@app\",\"roleName\":\"applicationAdmin\"}"),
"{\"error-code\":\"INTERNAL_SERVER_ERROR\",\"message\":\"NullPointerException\"}", 500);
// POST an application is allowed for a tenant operator.
tester.assertResponse(request("/application/v4/tenant/my-tenant/application/my-app", POST)
.user("operator@tenant")
- .roles(Set.of(roles.tenantOperator(id.tenant()))),
+ .roles(Set.of(Role.tenantOperator(id.tenant()))),
new File("application-created.json"));
// POST an application is not allowed under a different tenant.
tester.assertResponse(request("/application/v4/tenant/other-tenant/application/my-app", POST)
- .roles(Set.of(roles.tenantOperator(id.tenant()))),
+ .roles(Set.of(Role.tenantOperator(id.tenant()))),
accessDenied, 403);
// POST an application role is allowed for a tenant admin.
tester.assertResponse(request("/user/v1/tenant/my-tenant/application/my-app", POST)
- .roles(Set.of(roles.tenantAdmin(id.tenant())))
+ .roles(Set.of(Role.tenantAdmin(id.tenant())))
.data("{\"user\":\"reader@app\",\"roleName\":\"applicationReader\"}"),
"{\"message\":\"user 'reader@app' is now a member of role 'applicationReader' of 'my-app' owned by 'my-tenant'\"}");
// POST a tenant role is not allowed to an application.
tester.assertResponse(request("/user/v1/tenant/my-tenant/application/my-app", POST)
- .roles(Set.of(roles.hostedOperator()))
+ .roles(Set.of(Role.hostedOperator()))
.data("{\"user\":\"reader@app\",\"roleName\":\"tenantOperator\"}"),
"{\"error-code\":\"BAD_REQUEST\",\"message\":\"Malformed or illegal role name 'tenantOperator'.\"}", 400);
// GET tenant role information is available to application readers.
tester.assertResponse(request("/user/v1/tenant/my-tenant")
- .roles(Set.of(roles.applicationReader(id.tenant(), id.application()))),
+ .roles(Set.of(Role.applicationReader(id.tenant(), id.application()))),
new File("tenant-roles.json"));
// GET application role information is available to tenant operators.
tester.assertResponse(request("/user/v1/tenant/my-tenant/application/my-app")
- .roles(Set.of(roles.tenantOperator(id.tenant()))),
+ .roles(Set.of(Role.tenantOperator(id.tenant()))),
new File("application-roles.json"));
// GET application role information is available also under the /api prefix.
tester.assertResponse(request("/api/user/v1/tenant/my-tenant/application/my-app")
- .roles(Set.of(roles.tenantOperator(id.tenant()))),
+ .roles(Set.of(Role.tenantOperator(id.tenant()))),
new File("application-roles.json"));
// DELETE an application role is allowed for an application admin.
tester.assertResponse(request("/user/v1/tenant/my-tenant/application/my-app", DELETE)
- .roles(Set.of(roles.applicationAdmin(id.tenant(), id.application())))
+ .roles(Set.of(Role.applicationAdmin(id.tenant(), id.application())))
.data("{\"user\":\"operator@tenant\",\"roleName\":\"applicationAdmin\"}"),
"{\"message\":\"user 'operator@tenant' is no longer a member of role 'applicationAdmin' of 'my-app' owned by 'my-tenant'\"}");
// DELETE an application is available to application admins.
tester.assertResponse(request("/application/v4/tenant/my-tenant/application/my-app", DELETE)
- .roles(Set.of(roles.applicationAdmin(id.tenant(), id.application()))),
+ .roles(Set.of(Role.applicationAdmin(id.tenant(), id.application()))),
"");
// DELETE a tenant role is available to tenant admins.
tester.assertResponse(request("/user/v1/tenant/my-tenant", DELETE)
- .roles(Set.of(roles.tenantAdmin(id.tenant())))
+ .roles(Set.of(Role.tenantAdmin(id.tenant())))
.data("{\"user\":\"operator@tenant\",\"roleName\":\"tenantOperator\"}"),
"{\"message\":\"user 'operator@tenant' is no longer a member of role 'tenantOperator' of 'my-tenant'\"}");
@@ -155,7 +153,7 @@ public class UserApiTest extends ControllerContainerCloudTest {
// DELETE the tenant is available to the tenant owner.
tester.assertResponse(request("/application/v4/tenant/my-tenant", DELETE)
- .roles(Set.of(roles.tenantOwner(id.tenant()))),
+ .roles(Set.of(Role.tenantOwner(id.tenant()))),
new File("tenant-without-applications.json"));
}