summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorJon Marius Venstad <venstad@gmail.com>2019-04-04 11:02:13 +0200
committerJon Marius Venstad <venstad@gmail.com>2019-04-04 11:02:13 +0200
commit7e453002c4957397f20e0bd9e31651da674ddf7c (patch)
treeff0f8830d4f519378b384b3d5649156e55378180 /controller-server
parent77433506fc9af7a0b17a43a0e14ad6b0f1ea8594 (diff)
Move role package to controller-api
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilter.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/Auth0RoleFilter.java4
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java8
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Action.java43
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Context.java84
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/PathGroup.java176
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Policy.java130
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Privilege.java99
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Role.java128
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RoleInSystem.java8
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RoleInSystemWithTenant.java9
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RoleInSystemWithTenantAndApplication.java10
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RoleMembership.java73
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RolePrincipal.java15
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleFilterTest.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/role/PathGroupTest.java73
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/role/RoleMembershipTest.java74
18 files changed, 13 insertions, 935 deletions
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 1a3498e4abd..fee6bb7c44c 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
@@ -18,9 +18,9 @@ import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.TenantController;
import com.yahoo.vespa.hosted.controller.athenz.ApplicationAction;
import com.yahoo.vespa.hosted.controller.athenz.impl.AthenzFacade;
-import com.yahoo.vespa.hosted.controller.role.Role;
-import com.yahoo.vespa.hosted.controller.role.RoleMembership;
-import com.yahoo.vespa.hosted.controller.role.RolePrincipal;
+import com.yahoo.vespa.hosted.controller.api.role.Role;
+import com.yahoo.vespa.hosted.controller.api.role.RoleMembership;
+import com.yahoo.vespa.hosted.controller.api.role.RolePrincipal;
import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant;
import com.yahoo.vespa.hosted.controller.tenant.Tenant;
import com.yahoo.vespa.hosted.controller.tenant.UserTenant;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/Auth0RoleFilter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/Auth0RoleFilter.java
index 9596e1d10f9..25d5b72b7e4 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/Auth0RoleFilter.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/Auth0RoleFilter.java
@@ -2,8 +2,8 @@ package com.yahoo.vespa.hosted.controller.restapi.filter;
import com.yahoo.jdisc.http.filter.DiscFilterRequest;
import com.yahoo.jdisc.http.filter.security.base.JsonSecurityRequestFilterBase;
-import com.yahoo.vespa.hosted.controller.role.RoleMembership;
-import com.yahoo.vespa.hosted.controller.role.RolePrincipal;
+import com.yahoo.vespa.hosted.controller.api.role.RoleMembership;
+import com.yahoo.vespa.hosted.controller.api.role.RolePrincipal;
import java.util.Objects;
import java.util.Optional;
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 ecf6cd802ba..8c473bcaaec 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
@@ -10,10 +10,10 @@ import com.yahoo.jdisc.http.filter.security.cors.CorsFilterConfig;
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.role.Action;
-import com.yahoo.vespa.hosted.controller.role.Role;
-import com.yahoo.vespa.hosted.controller.role.RoleMembership;
-import com.yahoo.vespa.hosted.controller.role.RolePrincipal;
+import com.yahoo.vespa.hosted.controller.api.role.Action;
+import com.yahoo.vespa.hosted.controller.api.role.Role;
+import com.yahoo.vespa.hosted.controller.api.role.RoleMembership;
+import com.yahoo.vespa.hosted.controller.api.role.RolePrincipal;
import java.security.Principal;
import java.util.Optional;
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Action.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Action.java
deleted file mode 100644
index 533c28905a9..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Action.java
+++ /dev/null
@@ -1,43 +0,0 @@
-// 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.role;
-
-import com.yahoo.jdisc.http.HttpRequest;
-
-import java.util.EnumSet;
-
-/**
- * Action defines an operation, typically a HTTP method, that may be performed on an entity in the controller
- * (e.g. tenant or application).
- *
- * @author mpolden
- */
-public enum Action {
-
- create,
- read,
- update,
- delete;
-
- public static EnumSet<Action> all() {
- return EnumSet.allOf(Action.class);
- }
-
- public static EnumSet<Action> write() {
- return EnumSet.of(create, update, delete);
- }
-
- /** Returns the appropriate action for given HTTP method */
- public static Action from(HttpRequest.Method method) {
- switch (method) {
- case POST: return Action.create;
- case GET:
- case OPTIONS:
- case HEAD: return Action.read;
- case PUT:
- case PATCH: return Action.update;
- case DELETE: return Action.delete;
- default: throw new IllegalArgumentException("No action defined for method " + method);
- }
- }
-
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Context.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Context.java
deleted file mode 100644
index 71452a3ef20..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Context.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// 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.role;
-
-import com.yahoo.config.provision.ApplicationName;
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.config.provision.TenantName;
-
-import java.util.Objects;
-import java.util.Optional;
-
-/**
- * The context in which a role is valid.
- *
- * @author mpolden
- */
-public 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) {
- 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() {
- return tenant;
- }
-
- /** A specific application this is valid for, if any */
- public Optional<ApplicationName> application() {
- return application;
- }
-
- /** System in which this is valid */
- public SystemName system() {
- return system;
- }
-
- /** Returns whether this context is considered limited */
- public boolean limited() {
- return tenant.isPresent() || application.isPresent();
- }
-
- /** 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 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);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Context context = (Context) o;
- return tenant.equals(context.tenant) &&
- application.equals(context.application) &&
- system == context.system;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(tenant, application, system);
- }
-
- @Override
- public String toString() {
- return "tenant " + tenant.map(TenantName::value).orElse("[none]") + ", application " +
- application.map(ApplicationName::value).orElse("[none]") + ", system " + system;
- }
-
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/PathGroup.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/PathGroup.java
deleted file mode 100644
index 9dbfdbb8dac..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/PathGroup.java
+++ /dev/null
@@ -1,176 +0,0 @@
-// 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.role;
-
-import com.yahoo.restapi.Path;
-
-import java.net.URI;
-import java.util.EnumSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-
-/**
- * This declares and groups all known REST API paths in the controller.
- *
- * When creating a new API, its paths must be added here and a policy must be declared in {@link Policy}.
- *
- * @author mpolden
- */
-public enum PathGroup {
-
- /** Paths used for system management by operators. */
- operator("/controller/v1/{*}",
- "/flags/v1/{*}",
- "/nodes/v2/{*}",
- "/orchestrator/v1/{*}",
- "/os/v1/{*}",
- "/provision/v2/{*}",
- "/zone/v2/{*}"),
-
- /** Paths used for user management. */
- userManagement("/user/v1/{*}"), // TODO probably add tenant and application levels.
-
- /** Paths used for creating user tenants. */
- user("/application/v4/user"),
-
- /** Paths used for creating tenants with proper access control. */
- tenant(Matcher.tenant,
- "/application/v4/tenant/{tenant}"),
-
- /** Paths used by tenant administrators. */
- tenantInfo(Matcher.tenant,
- "/application/v4/tenant/{tenant}/application/"),
-
- /** Path for the base application resource. */
- application(Matcher.tenant,
- Matcher.application,
- "/application/v4/tenant/{tenant}/application/{application}"),
-
- /** Paths used by application administrators. */
- applicationInfo(Matcher.tenant,
- Matcher.application,
- "/application/v4/tenant/{tenant}/application/{application}/deploying/{*}",
- "/application/v4/tenant/{tenant}/application/{application}/instance/{*}",
- "/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/{*}"),
-
- /** Path used to restart application nodes. */ // TODO move to the above when everyone is on new pipeline.
- applicationRestart(Matcher.tenant,
- Matcher.application,
- "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{ignored}/restart"),
- /** Paths used for development deployments. */
- developmentDeployment(Matcher.tenant,
- Matcher.application,
- "/application/v4/tenant/{tenant}/application/{application}/environment/dev/region/{region}/instance/{instance}",
- "/application/v4/tenant/{tenant}/application/{application}/environment/dev/region/{region}/instance/{instance}/deploy",
- "/application/v4/tenant/{tenant}/application/{application}/environment/perf/region/{region}/instance/{instance}",
- "/application/v4/tenant/{tenant}/application/{application}/environment/perf/region/{region}/instance/{instance}/deploy"),
-
- /** Paths used for production deployments. */
- productionDeployment(Matcher.tenant,
- Matcher.application,
- "/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"),
-
- /** Paths used for continuous deployment to production. */
- submission(Matcher.tenant,
- Matcher.application,
- "/application/v4/tenant/{tenant}/application/{application}/submit"),
-
- /** Paths used for other tasks by build services. */ // TODO: This will vanish.
- buildService(Matcher.tenant,
- Matcher.application,
- "/application/v4/tenant/{tenant}/application/{application}/jobreport",
- "/application/v4/tenant/{tenant}/application/{application}/promote",
- "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/promote"),
-
- /** Paths which contain (not very strictly) classified information about, e.g., customers. */
- classifiedInfo("/athenz/v1/{*}",
- "/cost/v1/{*}",
- "/deployment/v1/{*}",
- "/application/v4/",
- "/application/v4/tenant/",
- "/",
- "/d/{*}",
- "/statuspage/v1/{*}"
- ),
-
- /** Paths providing public information. */
- publicInfo("/badge/v1/{*}",
- "/zone/v1/{*}");
-
- final List<String> pathSpecs;
- final List<Matcher> matchers;
-
- PathGroup(String... pathSpecs) {
- this(List.of(), List.of(pathSpecs));
- }
-
- PathGroup(Matcher first, String... pathSpecs) {
- this(List.of(first), List.of(pathSpecs));
- }
-
- PathGroup(Matcher first, Matcher second, String... pathSpecs) {
- this(List.of(first, second), 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, List<String> pathSpecs) {
- this.matchers = matchers;
- this.pathSpecs = pathSpecs;
- }
-
- /** Returns path if it matches any spec in this group, with match groups set by the match. */
- @SuppressWarnings("deprecation")
- private Optional<Path> get(URI uri) {
- Path matcher = new Path(uri); // TODO Get URI down here.
- for (String spec : pathSpecs) // Iterate to be sure the Path's state is that of the match.
- if (matcher.matches(spec)) return Optional.of(matcher);
- return Optional.empty();
- }
-
- /** All known path groups */
- public 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) {
- return get(uri).map(p -> {
- boolean match = true;
- String tenant = p.get(Matcher.tenant.name);
- if (tenant != null && context.tenant().isPresent()) {
- match = context.tenant().get().value().equals(tenant);
- }
- String application = p.get(Matcher.application.name);
- if (application != null && context.application().isPresent()) {
- match &= context.application().get().value().equals(application);
- }
- return match;
- }).orElse(false);
- }
-
-
- /** Fragments used to match parts of a path to create a context. */
- enum Matcher {
-
- tenant("{tenant}"),
- application("{application}");
-
- final String pattern;
- final String name;
-
- Matcher(String pattern) {
- this.pattern = pattern;
- this.name = pattern.substring(1, pattern.length() - 1);
- }
-
- }
-
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Policy.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Policy.java
deleted file mode 100644
index 719f90b9fa4..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Policy.java
+++ /dev/null
@@ -1,130 +0,0 @@
-// 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.role;
-
-import com.yahoo.config.provision.ApplicationName;
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.config.provision.TenantName;
-
-import java.net.URI;
-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}.
- *
- * @author mpolden
- */
-public enum Policy {
-
- /** Full access to everything. */
- operator(Privilege.grant(Action.all())
- .on(PathGroup.all())
- .in(SystemName.all())),
-
- /** Full access to user management in select systems. */
- manager(Privilege.grant(Action.all())
- .on(PathGroup.userManagement)
- .in(SystemName.Public)),
-
- /** Access to create a user tenant in select systems. */
- userCreate(Privilege.grant(Action.update)
- .on(PathGroup.user)
- .in(SystemName.main, SystemName.cd, SystemName.dev)),
-
- /** Access to create a tenant in select systems. */
- tenantCreate(Privilege.grant(Action.create)
- .on(PathGroup.tenant)
- .in(SystemName.main, SystemName.cd, SystemName.dev)), // TODO SystemName.all()
-
- /** Full access to tenant information and settings. */
- tenantDelete(Privilege.grant(Action.delete)
- .on(PathGroup.tenant)
- .in(SystemName.all())),
-
- /** Full access to tenant information and settings. */
- tenantUpdate(Privilege.grant(Action.update)
- .on(PathGroup.tenant)
- .in(SystemName.all())),
-
- /** Read access to tenant information and settings. */
- tenantRead(Privilege.grant(Action.read)
- .on(PathGroup.tenant, PathGroup.tenantInfo)
- .in(SystemName.all())),
-
- /** Access to create application under a certain tenant. */
- applicationCreate(Privilege.grant(Action.create)
- .on(PathGroup.application)
- .in(SystemName.all())),
-
- /** Read access to application information and settings. */
- applicationRead(Privilege.grant(Action.read)
- .on(PathGroup.application, PathGroup.applicationInfo)
- .in(SystemName.all())),
-
- /** Read access to application information and settings. */
- applicationUpdate(Privilege.grant(Action.update)
- .on(PathGroup.application, PathGroup.applicationInfo)
- .in(SystemName.all())),
-
- /** Access to delete a certain application. */
- applicationDelete(Privilege.grant(Action.delete)
- .on(PathGroup.application)
- .in(SystemName.all())),
-
- /** Full access to application information and settings. */
- applicationOperations(Privilege.grant(Action.write())
- .on(PathGroup.applicationInfo, PathGroup.applicationRestart)
- .in(SystemName.all())),
-
- /** Full access to application development deployments. */
- developmentDeployment(Privilege.grant(Action.all())
- .on(PathGroup.developmentDeployment)
- .in(SystemName.all())),
-
- /** Full access to application production deployments. */
- productionDeployment(Privilege.grant(Action.all())
- .on(PathGroup.productionDeployment)
- .in(SystemName.all())),
-
- /** Read access to all application deployments. */
- deploymentRead(Privilege.grant(Action.read)
- .on(PathGroup.developmentDeployment, PathGroup.productionDeployment)
- .in(SystemName.all())),
-
- /** Full access to submissions for continuous deployment. */
- submission(Privilege.grant(Action.all())
- .on(PathGroup.submission)
- .in(SystemName.all())),
-
- /** Full access to the additional tasks needed for continuous deployment. */
- deploymentPipeline(Privilege.grant(Action.all()) // TODO remove when everyone is on new pipeline.
- .on(PathGroup.buildService, PathGroup.applicationRestart)
- .in(SystemName.all())),
-
- /** Read access to all information in select systems. */
- classifiedRead(Privilege.grant(Action.read)
- .on(PathGroup.all())
- .in(SystemName.main, SystemName.cd, SystemName.dev)),
-
- /** Read access to public info. */
- publicRead(Privilege.grant(Action.read)
- .on(PathGroup.publicInfo)
- .in(SystemName.all()));
-
- private final Set<Privilege> privileges;
-
- Policy(Privilege... privileges) {
- this.privileges = Set.of(privileges);
- }
-
- /** Returns whether action is allowed on path in given context */
- public boolean evaluate(Action action, URI uri, Context context) {
- return privileges.stream().anyMatch(privilege -> privilege.actions().contains(action) &&
- privilege.systems().contains(context.system()) &&
- privilege.pathGroups().stream()
- .anyMatch(pg -> pg.matches(uri, context)));
- }
-
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Privilege.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Privilege.java
deleted file mode 100644
index 4c5ad136f56..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Privilege.java
+++ /dev/null
@@ -1,99 +0,0 @@
-// 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.role;
-
-import com.yahoo.config.provision.SystemName;
-
-import java.util.EnumSet;
-import java.util.LinkedHashSet;
-import java.util.Objects;
-import java.util.Set;
-
-/**
- * This describes a privilege in the controller. A privilege expresses the actions (e.g. create or read) granted
- * for a particular group of REST API paths. A privilege is valid in one or more systems.
- *
- * @author mpolden
- */
-public class Privilege {
-
- private final Set<SystemName> systems;
- private final Set<Action> actions;
- private final Set<PathGroup> pathGroups;
-
- private Privilege(Set<SystemName> systems, Set<Action> actions, Set<PathGroup> pathGroups) {
- this.systems = EnumSet.copyOf(Objects.requireNonNull(systems, "system must be non-null"));
- this.actions = EnumSet.copyOf(Objects.requireNonNull(actions, "actions must be non-null"));
- this.pathGroups = EnumSet.copyOf(Objects.requireNonNull(pathGroups, "pathGroups must be non-null"));
- if (systems.isEmpty()) {
- throw new IllegalArgumentException("systems must be non-empty");
- }
- }
-
- /** Systems where this applies */
- public Set<SystemName> systems() {
- return systems;
- }
-
- /** Actions allowed by this */
- public Set<Action> actions() {
- return actions;
- }
-
- /** Path groups where this applies */
- public Set<PathGroup> pathGroups() {
- return pathGroups;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- Privilege privilege = (Privilege) o;
- return systems.equals(privilege.systems) &&
- actions.equals(privilege.actions) &&
- pathGroups.equals(privilege.pathGroups);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(systems, actions, pathGroups);
- }
-
- public static PrivilegeBuilder grant(Action... actions) {
- return grant(Set.of(actions));
- }
-
- public static PrivilegeBuilder grant(Set<Action> actions) {
- return new PrivilegeBuilder(actions);
- }
-
- public static class PrivilegeBuilder {
-
- private Set<Action> actions;
- private Set<PathGroup> pathGroups;
-
- private PrivilegeBuilder(Set<Action> actions) {
- this.actions = EnumSet.copyOf(actions);
- this.pathGroups = new LinkedHashSet<>();
- }
-
- public PrivilegeBuilder on(PathGroup... pathGroups) {
- return on(Set.of(pathGroups));
- }
-
- public PrivilegeBuilder on(Set<PathGroup> pathGroups) {
- this.pathGroups.addAll(pathGroups);
- return this;
- }
-
- public Privilege in(SystemName... systems) {
- return in(Set.of(systems));
- }
-
- public Privilege in(Set<SystemName> systems) {
- return new Privilege(systems, actions, pathGroups);
- }
-
- }
-
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Role.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Role.java
deleted file mode 100644
index d3f43ad895f..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/Role.java
+++ /dev/null
@@ -1,128 +0,0 @@
-// 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.role;
-
-import java.net.URI;
-import com.yahoo.config.provision.ApplicationName;
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.config.provision.TenantName;
-
-import java.util.EnumSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * This declares all tenant roles known to the controller. A role contains one or more {@link Policy}s which decide
- * what actions a member of a role can perform.
- *
- * Optionally, some role definition also inherit all policies from a "lower ranking" role. Read the list of roles
- * from {@code everyone} to {@code tenantAdmin}, in order, to see what policies these roles.
- *
- * @author mpolden
- * @author jonmv
- */
-public class Role implements RoleInSystem, RoleInSystemWithTenant, RoleInSystemWithTenantAndApplication {
-
- /** Deus ex machina. */
- public static final RoleInSystem hostedOperator = new Role(Policy.operator);
-
- /** Build service which may submit new applications for continuous deployment. */
- public static final RoleInSystemWithTenantAndApplication buildService = new Role(Policy.submission,
- Policy.applicationRead);
-
- /** Base role which every user is part of. */
- public static final RoleInSystem everyone = new Role(Policy.classifiedRead,
- Policy.publicRead,
- Policy.userCreate,
- Policy.tenantCreate);
-
- /** Application reader which can see all information about an application, its tenant and deployments. */
- public static final RoleInSystemWithTenantAndApplication applicationReader = new Role(everyone,
- Policy.tenantRead,
- Policy.applicationRead,
- Policy.deploymentRead);
-
- /** Application developer with access to deploy to development zones. */
- public static final RoleInSystemWithTenantAndApplication applicationDeveloper = new Role(applicationReader,
- Policy.developmentDeployment);
-
- /** Application operator with access to normal, operational tasks of an application. */
- public static final RoleInSystemWithTenantAndApplication applicationOperator = new Role(applicationDeveloper,
- Policy.applicationOperations);
-
- /** Application administrator with full access to an already existing application, including emergency operations. */
- public static final RoleInSystemWithTenantAndApplication applicationAdmin = new Role(applicationOperator,
- Policy.applicationUpdate,
- Policy.productionDeployment,
- Policy.submission);
-
- /** Application administrator with the additional ability to delete an application. */
- public static final RoleInSystemWithTenantAndApplication applicationOwner = new Role(applicationOperator,
- Policy.applicationDelete);
-
- /** Tenant operator with admin access to all applications under the tenant, as well as the ability to create applications. */
- public static final RoleInSystemWithTenant tenantOperator = new Role(applicationAdmin,
- Policy.applicationCreate);
-
- /** Tenant admin with full access to all tenant resources, except deleting the tenant. */
- public static final RoleInSystemWithTenant tenantAdmin = new Role(tenantOperator,
- Policy.applicationDelete,
- Policy.manager,
- Policy.tenantUpdate);
-
- /** Tenant admin with full access to all tenant resources. */
- public static final RoleInSystemWithTenant tenantOwner = new Role(tenantAdmin,
- Policy.tenantDelete);
-
- /** Build and continuous delivery service. */ // TODO replace with buildService, when everyone is on new pipeline.
- public static final RoleInSystemWithTenantAndApplication tenantPipeline = new Role(everyone,
- Policy.submission,
- Policy.deploymentPipeline,
- Policy.productionDeployment);
-
- /** Tenant administrator with full access to all child resources. */
- public static final RoleInSystemWithTenant athenzTenantAdmin = new Role(everyone,
- Policy.tenantRead,
- Policy.tenantUpdate,
- Policy.tenantDelete,
- Policy.applicationCreate,
- Policy.applicationUpdate,
- Policy.applicationDelete,
- Policy.applicationOperations,
- Policy.developmentDeployment);
-
- private final Set<Policy> policies;
-
- private Role(Policy... policies) {
- this.policies = EnumSet.copyOf(Set.of(policies));
- }
-
- private Role(Object inherited, Policy... policies) {
- this.policies = EnumSet.copyOf(Set.of(policies));
- this.policies.addAll(((Role) inherited).policies);
- }
-
- /**
- * Returns whether this role is allowed to perform action in given role context. Action is allowed if at least one
- * policy evaluates to true.
- */
- public boolean allows(Action action, URI uri, Context context) {
- return policies.stream().anyMatch(policy -> policy.evaluate(action, uri, context));
- }
-
- @Override
- public RoleMembership limitedTo(SystemName system) {
- return new RoleMembership(Map.of(this, Set.of(Context.unlimitedIn(system))));
- }
-
- @Override
- public RoleMembership limitedTo(TenantName tenant, SystemName system) {
- return new RoleMembership(Map.of(this, Set.of(Context.limitedTo(tenant, system))));
- }
-
- @Override
- public RoleMembership limitedTo(ApplicationName application, TenantName tenant, SystemName system) {
- return new RoleMembership(Map.of(this, Set.of(Context.limitedTo(tenant, application, system))));
- }
-
-}
-
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RoleInSystem.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RoleInSystem.java
deleted file mode 100644
index 8f5401b2993..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RoleInSystem.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package com.yahoo.vespa.hosted.controller.role;
-
-import com.yahoo.config.provision.SystemName;
-
-/** A role which requires only the context of a system. */
-public interface RoleInSystem {
- RoleMembership limitedTo(SystemName system);
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RoleInSystemWithTenant.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RoleInSystemWithTenant.java
deleted file mode 100644
index c5bba56b640..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RoleInSystemWithTenant.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package com.yahoo.vespa.hosted.controller.role;
-
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.config.provision.TenantName;
-
-/** A role which requires the context of a system and a tenant. */
-public interface RoleInSystemWithTenant {
- RoleMembership limitedTo(TenantName tenant, SystemName system);
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RoleInSystemWithTenantAndApplication.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RoleInSystemWithTenantAndApplication.java
deleted file mode 100644
index cb4b0bd4dd8..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RoleInSystemWithTenantAndApplication.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package com.yahoo.vespa.hosted.controller.role;
-
-import com.yahoo.config.provision.ApplicationName;
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.config.provision.TenantName;
-
-/** A role which requires the context of a system, a tenant, and an application. */
-public interface RoleInSystemWithTenantAndApplication {
- RoleMembership limitedTo(ApplicationName application, TenantName tenant, SystemName system);
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RoleMembership.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RoleMembership.java
deleted file mode 100644
index e0311bebbba..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RoleMembership.java
+++ /dev/null
@@ -1,73 +0,0 @@
-// 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.role;
-
-import java.net.URI;
-import java.util.Collections;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-/**
- * A list of roles and their associated contexts. This defines the role membership of a tenant, and in which contexts
- * (see {@link Context}) those roles apply.
- *
- * @author mpolden
- * @author jonmv
- */
-public class RoleMembership {
-
- private final Map<Role, Set<Context>> roles;
-
- RoleMembership(Map<Role, Set<Context>> roles) {
- this.roles = roles.entrySet().stream()
- .collect(Collectors.toUnmodifiableMap(entry -> entry.getKey(),
- entry -> Set.copyOf(entry.getValue())));
- }
-
- public RoleMembership and(RoleMembership other) {
- return new RoleMembership(Stream.concat(this.roles.entrySet().stream(),
- other.roles.entrySet().stream())
- .collect(Collectors.toMap(Map.Entry::getKey,
- Map.Entry::getValue,
- (set1, set2) -> Stream.concat(set1.stream(), set2.stream())
- .collect(Collectors.toUnmodifiableSet()))));
- }
-
- /**
- * Returns whether any role in this allows action to take place in path
- */
- public boolean allows(Action action, URI uri) {
- return roles.entrySet().stream().anyMatch(kv -> {
- Role role = kv.getKey();
- Set<Context> contexts = kv.getValue();
- return contexts.stream().anyMatch(context -> role.allows(action, uri, context));
- });
- }
-
- /**
- * Returns the set of contexts for which the given role is valid.
- */
- public Set<Context> contextsFor(Object role) { // TODO fix.
- return roles.getOrDefault((Role) role, Collections.emptySet());
- }
-
- @Override
- public String toString() {
- return "roles " + roles;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if ( ! (o instanceof RoleMembership)) return false;
- return Objects.equals(roles, ((RoleMembership) o).roles);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(roles);
- }
-
-}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RolePrincipal.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RolePrincipal.java
deleted file mode 100644
index afb8a99d94a..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/role/RolePrincipal.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package com.yahoo.vespa.hosted.controller.role;
-
-import java.security.Principal;
-
-/**
- * A {@link Principal} with a {@link RoleMembership}.
- *
- * @author jonmv
- */
-public interface RolePrincipal extends Principal {
-
- /** Returns the roles with context this principal is a member of. */
- RoleMembership roles();
-
-}
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 b0084c38754..e862e12a18b 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
@@ -15,7 +15,7 @@ import com.yahoo.vespa.hosted.controller.athenz.HostedAthenzIdentities;
import com.yahoo.vespa.hosted.controller.athenz.impl.AthenzFacade;
import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzClientFactoryMock;
import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzDbMock;
-import com.yahoo.vespa.hosted.controller.role.Role;
+import com.yahoo.vespa.hosted.controller.api.role.Role;
import org.junit.Before;
import org.junit.Test;
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 4e8ef8128ec..459320741a9 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
@@ -8,9 +8,9 @@ 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.restapi.ApplicationRequestToDiscFilterRequestWrapper;
-import com.yahoo.vespa.hosted.controller.role.Role;
-import com.yahoo.vespa.hosted.controller.role.RoleMembership;
-import com.yahoo.vespa.hosted.controller.role.RolePrincipal;
+import com.yahoo.vespa.hosted.controller.api.role.Role;
+import com.yahoo.vespa.hosted.controller.api.role.RoleMembership;
+import com.yahoo.vespa.hosted.controller.api.role.RolePrincipal;
import org.junit.Test;
import java.io.IOException;
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/role/PathGroupTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/role/PathGroupTest.java
deleted file mode 100644
index b4a3e674594..00000000000
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/role/PathGroupTest.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package com.yahoo.vespa.hosted.controller.role;
-
-import org.junit.Test;
-
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-/**
- * @author jonmv
- * @author mpolden
- */
-public class PathGroupTest {
-
- @Test
- public void uniqueMatches() {
- // Ensure that each path group contains at most one match for any given path, to avoid undefined context extraction.
- Set<String> checkedAgainstSelf = new HashSet<>();
- for (PathGroup group1 : PathGroup.values())
- for (PathGroup group2 : PathGroup.values())
- for (String path1 : group1.pathSpecs)
- for (String path2 : group2.pathSpecs) {
- if (path1.equals(path2)) {
- if (checkedAgainstSelf.add(path1)) continue;
- fail("Path '" + path1 + "' appears in both '" + group1 + "' and '" + group2 + "'.");
- }
-
- String[] parts1 = path1.split("/");
- String[] parts2 = path2.split("/");
-
- int end = Math.min(parts1.length, parts2.length);
- // If one path has more parts than the other ...
- // and the other doesn't end with a wildcard matcher ...
- // and the longest one isn't just one wildcard longer ...
- // then one is strictly longer than the other, and it's not a match.
- if (end < parts1.length && (end == 0 || ! parts2[end - 1].equals("{*}")) && ! parts1[end].equals("{*}")) continue;
- if (end < parts2.length && (end == 0 || ! parts1[end - 1].equals("{*}")) && ! parts2[end].equals("{*}")) continue;
-
- int i;
- for (i = 0; i < end; i++)
- if ( ! parts1[i].equals(parts2[i])
- && ! (parts1[i].startsWith("{") && parts1[i].endsWith("}"))
- && ! (parts2[i].startsWith("{") && parts2[i].endsWith("}"))) break;
-
- if (i == end) fail("Paths '" + path1 + "' and '" + path2 + "' overlap.");
- }
-
- assertEquals(PathGroup.all().stream().mapToInt(group -> group.pathSpecs.size()).sum(),
- checkedAgainstSelf.size());
- }
-
- @Test
- public void contextMatches() {
- for (PathGroup group : PathGroup.values())
- for (String spec : group.pathSpecs) {
- for (PathGroup.Matcher matcher : PathGroup.Matcher.values()) {
- if (group.matchers.contains(matcher)) {
- if ( ! spec.contains(matcher.pattern))
- fail("Spec '" + spec + "' in '" + group.name() + "' should contain matcher '" + matcher.pattern + "'.");
- if (spec.replaceFirst(Pattern.quote(matcher.pattern), "").contains(matcher.pattern))
- fail("Spec '" + spec + "' in '" + group.name() + "' contains more than one instance of '" + matcher.pattern + "'.");
- }
- else if (spec.contains(matcher.pattern))
- fail("Spec '" + spec + "' in '" + group.name() + "' should not contain matcher '" + matcher.pattern + "'.");
- }
- }
- }
-
-}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/role/RoleMembershipTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/role/RoleMembershipTest.java
deleted file mode 100644
index 26fc095c1d3..00000000000
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/role/RoleMembershipTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-// 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.role;
-
-import com.yahoo.config.provision.ApplicationName;
-import com.yahoo.config.provision.SystemName;
-import com.yahoo.config.provision.TenantName;
-import org.junit.Test;
-
-import java.net.URI;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-/**
- * @author mpolden
- */
-public class RoleMembershipTest {
-
- @Test
- public void operator_membership() {
- RoleMembership roles = Role.hostedOperator.limitedTo(SystemName.main);
-
- // Operator actions
- assertFalse(roles.allows(Action.create, URI.create("/not/explicitly/defined")));
- assertTrue(roles.allows(Action.create, URI.create("/controller/v1/foo")));
- assertTrue(roles.allows(Action.update, URI.create("/os/v1/bar")));
- assertTrue(roles.allows(Action.update, URI.create("/application/v4/tenant/t1/application/a1")));
- assertTrue(roles.allows(Action.update, URI.create("/application/v4/tenant/t2/application/a2")));
- }
-
- @Test
- public void tenant_membership() {
- RoleMembership roles = Role.athenzTenantAdmin.limitedTo(TenantName.from("t1"), SystemName.main);
- assertFalse(roles.allows(Action.create, URI.create("/not/explicitly/defined")));
- assertFalse("Deny access to operator API", roles.allows(Action.create, URI.create("/controller/v1/foo")));
- assertFalse("Deny access to other tenant and app", roles.allows(Action.update, URI.create("/application/v4/tenant/t2/application/a2")));
- assertTrue(roles.allows(Action.update, URI.create("/application/v4/tenant/t1/application/a1")));
-
- RoleMembership multiContext = Role.athenzTenantAdmin.limitedTo(TenantName.from("t1"), SystemName.main)
- .and(Role.athenzTenantAdmin.limitedTo(TenantName.from("t2"), SystemName.main));
- assertFalse("Deny access to other tenant and app", multiContext.allows(Action.update, URI.create("/application/v4/tenant/t3/application/a3")));
- assertTrue(multiContext.allows(Action.update, URI.create("/application/v4/tenant/t2/application/a2")));
- assertTrue(multiContext.allows(Action.update, URI.create("/application/v4/tenant/t1/application/a1")));
-
- RoleMembership publicSystem = Role.athenzTenantAdmin.limitedTo(TenantName.from("t1"), SystemName.vaas);
- assertFalse(publicSystem.allows(Action.read, URI.create("/controller/v1/foo")));
- assertTrue(publicSystem.allows(Action.read, URI.create("/badge/v1/badge")));
- assertTrue(multiContext.allows(Action.update, URI.create("/application/v4/tenant/t1/application/a1")));
- }
-
- @Test
- public void build_service_membership() {
- RoleMembership roles = Role.tenantPipeline.limitedTo(ApplicationName.from("a1"), TenantName.from("t1"), SystemName.vaas);
- assertFalse(roles.allows(Action.create, URI.create("/not/explicitly/defined")));
- assertFalse(roles.allows(Action.update, URI.create("/application/v4/tenant/t1/application/a1")));
- assertTrue(roles.allows(Action.create, URI.create("/application/v4/tenant/t1/application/a1/jobreport")));
- assertFalse("No global read access", roles.allows(Action.read, URI.create("/controller/v1/foo")));
- }
-
- @Test
- public void multi_role_membership() {
- RoleMembership roles = Role.athenzTenantAdmin.limitedTo(TenantName.from("t1"), SystemName.main)
- .and(Role.tenantPipeline.limitedTo(ApplicationName.from("a1"), TenantName.from("t1"), SystemName.main));
- assertFalse(roles.allows(Action.create, URI.create("/not/explicitly/defined")));
- assertFalse(roles.allows(Action.create, URI.create("/controller/v1/foo")));
- assertTrue(roles.allows(Action.create, URI.create("/application/v4/tenant/t1/application/a1/jobreport")));
- assertTrue(roles.allows(Action.update, URI.create("/application/v4/tenant/t1/application/a1")));
- assertTrue("Global read access", roles.allows(Action.read, URI.create("/controller/v1/foo")));
- assertTrue("Dashboard read access", roles.allows(Action.read, URI.create("/")));
- assertTrue("Dashboard read access", roles.allows(Action.read, URI.create("/d/nodes")));
- assertTrue("Dashboard read access", roles.allows(Action.read, URI.create("/statuspage/v1/incidents")));
- }
-
-}