From f83e2033db821695a7984577a1ee18c617fc0b55 Mon Sep 17 00:00:00 2001 From: Jon Marius Venstad Date: Thu, 4 Apr 2019 16:35:27 +0200 Subject: Replace RoleMembership with a bound Role, using enum ProtoRole for declarations --- .../controller/api/role/ApplicationRole.java | 29 +++++ .../hosted/controller/api/role/ProtoRole.java | 101 +++++++++++++++++ .../vespa/hosted/controller/api/role/Role.java | 126 ++++----------------- .../hosted/controller/api/role/RoleInSystem.java | 8 -- .../api/role/RoleInSystemWithTenant.java | 9 -- .../role/RoleInSystemWithTenantAndApplication.java | 10 -- .../hosted/controller/api/role/RoleMembership.java | 73 ------------ .../vespa/hosted/controller/api/role/Roles.java | 88 ++++++++++++++ .../controller/api/role/SecurityContext.java | 13 ++- .../hosted/controller/api/role/TenantRole.java | 25 ++++ .../hosted/controller/api/role/UnboundRole.java | 21 ++++ .../controller/api/role/RoleMembershipTest.java | 74 ------------ .../vespa/hosted/controller/api/role/RoleTest.java | 54 +++++++++ .../restapi/filter/AthenzRoleFilter.java | 22 ++-- .../filter/ControllerAuthorizationFilter.java | 14 +-- .../restapi/filter/AthenzRoleFilterTest.java | 65 ++++++----- .../filter/ControllerAuthorizationFilterTest.java | 15 ++- 17 files changed, 410 insertions(+), 337 deletions(-) create mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/ApplicationRole.java create mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/ProtoRole.java delete mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleInSystem.java delete mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleInSystemWithTenant.java delete mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleInSystemWithTenantAndApplication.java delete mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleMembership.java create mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Roles.java create mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/TenantRole.java create mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/UnboundRole.java delete mode 100644 controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleMembershipTest.java create mode 100644 controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleTest.java 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 new file mode 100644 index 00000000000..b1018fb08a6 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/ApplicationRole.java @@ -0,0 +1,29 @@ +package com.yahoo.vespa.hosted.controller.api.role; + +import com.yahoo.config.provision.ApplicationName; +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}. + * + * @author jonmv + */ +public class ApplicationRole extends Role { + + ApplicationRole(ProtoRole protoRole, SystemName system, TenantName tenant, ApplicationName application) { + super(protoRole, Context.limitedTo(tenant, application, system)); + } + + /** Returns the {@link TenantName} this is bound to. */ + public TenantName tenant() { return context.tenant().get(); } + + /** Returns the {@link ApplicationName} this is bound to. */ + public ApplicationName application() { return context.application().get(); } + + @Override + public String toString() { + return "role '" + proto() + "' of '" + application() + "' owned by '" + tenant() + "'"; + } + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/ProtoRole.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/ProtoRole.java new file mode 100644 index 00000000000..44d0d2356bd --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/ProtoRole.java @@ -0,0 +1,101 @@ +package com.yahoo.vespa.hosted.controller.api.role; + +import java.util.EnumSet; +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. + * + * See {@link Role} for roles bound to a context, where policies can be evaluated. + * + * @author mpolden + * @author jonmv + */ +public enum ProtoRole { + + /** Deus ex machina. */ + hostedOperator(Policy.operator), + + /** Build service which may submit new applications for continuous deployment. */ + buildService(Policy.submission, + Policy.applicationRead), + + /** Base role which every user is part of. */ + everyone(Policy.classifiedRead, + Policy.publicRead, + Policy.userCreate, + Policy.tenantCreate), + + /** Application reader which can see all information about an application, its tenant and deployments. */ + applicationReader(everyone, + Policy.tenantRead, + Policy.applicationRead, + Policy.deploymentRead), + + /** Application developer with access to deploy to development zones. */ + applicationDeveloper(applicationReader, + Policy.developmentDeployment), + + /** Application operator with access to normal, operational tasks of an application. */ + applicationOperator(applicationDeveloper, + Policy.applicationOperations), + + /** Application administrator with full access to an already existing application, including emergency operations. */ + applicationAdmin(applicationOperator, + Policy.applicationUpdate, + Policy.productionDeployment, + Policy.submission), + + /** Application administrator with the additional ability to delete an application. */ + applicationOwner(applicationOperator, + Policy.applicationDelete), + + /** Tenant operator with admin access to all applications under the tenant, as well as the ability to create applications. */ + tenantOperator(applicationAdmin, + Policy.applicationCreate), + + /** Tenant admin with full access to all tenant resources, except deleting the tenant. */ + tenantAdmin(tenantOperator, + Policy.applicationDelete, + Policy.manager, + Policy.tenantUpdate), + + /** Tenant admin with full access to all tenant resources. */ + tenantOwner(tenantAdmin, + Policy.tenantDelete), + + /** Build and continuous delivery service. */ // TODO replace with buildService, when everyone is on new pipeline. + tenantPipeline(everyone, + Policy.submission, + Policy.deploymentPipeline, + Policy.productionDeployment), + + /** Tenant administrator with full access to all child resources. */ + athenzTenantAdmin(everyone, + Policy.tenantRead, + Policy.tenantUpdate, + Policy.tenantDelete, + Policy.applicationCreate, + Policy.applicationUpdate, + Policy.applicationDelete, + Policy.applicationOperations, + Policy.developmentDeployment); + + private final Set policies; + + ProtoRole(Policy... policies) { + this.policies = EnumSet.copyOf(Set.of(policies)); + } + + ProtoRole(ProtoRole inherited, Policy... policies) { + this.policies = EnumSet.copyOf(Set.of(policies)); + this.policies.addAll(inherited.policies); + } + + Set policies() { return policies; } + +} 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 e452464dcd4..c98ddd5a37c 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Role.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Role.java @@ -2,126 +2,46 @@ package com.yahoo.vespa.hosted.controller.api.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.Objects; -import java.util.EnumSet; -import java.util.Map; -import java.util.Set; +import static java.util.Objects.requireNonNull; /** - * 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. + * A role is a combination of a {@link ProtoRole} and a {@link Context}, which allows evaluation + * of access control for a given action on a resource. Create using {@link Roles}. * - * 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); +public abstract class Role { - /** 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); + private final ProtoRole protoRole; + final Context context; - /** 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 policies; - - private Role(Policy... policies) { - this.policies = EnumSet.copyOf(Set.of(policies)); + Role(ProtoRole protoRole, Context context) { + this.protoRole = requireNonNull(protoRole); + this.context = requireNonNull(context); } - 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)); - } + /** Returns the proto role of this role. */ + public ProtoRole proto() { return protoRole; } - @Override - public RoleMembership limitedTo(SystemName system) { - return new RoleMembership(Map.of(this, Set.of(Context.unlimitedIn(system)))); + /** Returns whether this role is allowed to perform the given action on the given resource. */ + public boolean allows(Action action, URI uri) { + return protoRole.policies().stream().anyMatch(policy -> policy.evaluate(action, uri, context)); } @Override - public RoleMembership limitedTo(TenantName tenant, SystemName system) { - return new RoleMembership(Map.of(this, Set.of(Context.limitedTo(tenant, system)))); + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Role role = (Role) o; + return protoRole == role.protoRole && + Objects.equals(context, role.context); } @Override - public RoleMembership limitedTo(ApplicationName application, TenantName tenant, SystemName system) { - return new RoleMembership(Map.of(this, Set.of(Context.limitedTo(tenant, application, system)))); + public int hashCode() { + return Objects.hash(protoRole, context); } } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleInSystem.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleInSystem.java deleted file mode 100644 index 144a02c4987..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleInSystem.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.yahoo.vespa.hosted.controller.api.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-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleInSystemWithTenant.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleInSystemWithTenant.java deleted file mode 100644 index ab8aa29b7cc..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleInSystemWithTenant.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.yahoo.vespa.hosted.controller.api.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-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleInSystemWithTenantAndApplication.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleInSystemWithTenantAndApplication.java deleted file mode 100644 index 31a903c998a..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleInSystemWithTenantAndApplication.java +++ /dev/null @@ -1,10 +0,0 @@ -package com.yahoo.vespa.hosted.controller.api.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-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleMembership.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/RoleMembership.java deleted file mode 100644 index f046b0d7203..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/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.api.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> roles; - - RoleMembership(Map> 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 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 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-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 new file mode 100644 index 00000000000..92374dfff74 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/Roles.java @@ -0,0 +1,88 @@ +package com.yahoo.vespa.hosted.controller.api.role; + +import com.yahoo.config.provision.ApplicationName; +import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.TenantName; + +import java.util.Objects; + +/** + * Use if you need to create {@link Role}s for its system. + * + * This also defines the relationship between {@link ProtoRole}s and their required {@link Context}s. + * + * @author jonmv + */ +public class Roles { + + private final SystemName system; + + /** Create 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 ProtoRole#hostedOperator} for the current system. */ + public UnboundRole hostedOperator() { + return new UnboundRole(ProtoRole.hostedOperator, system); + } + + /** Returns a {@link ProtoRole#everyone} for the current system. */ + public UnboundRole everyone() { + return new UnboundRole(ProtoRole.everyone, system); + } + + // Athenz based roles. + /** Returns a {@link ProtoRole#athenzTenantAdmin} for the current system and given tenant. */ + public TenantRole athenzTenantAdmin(TenantName tenant) { + return new TenantRole(ProtoRole.athenzTenantAdmin, system, tenant); + } + + /** Returns a {@link ProtoRole#tenantPipeline} for the current system and given tenant and application. */ + public ApplicationRole tenantPipeline(TenantName tenant, ApplicationName application) { + return new ApplicationRole(ProtoRole.tenantPipeline, system, tenant, application); + } + + // Other identity provider based roles. + /** Returns a {@link ProtoRole#tenantOwner} for the current system and given tenant. */ + public TenantRole tenantOwner(TenantName tenant) { + return new TenantRole(ProtoRole.tenantOwner, system, tenant); + } + + /** Returns a {@link ProtoRole#tenantAdmin} for the current system and given tenant. */ + public TenantRole tenantAdmin(TenantName tenant) { + return new TenantRole(ProtoRole.tenantAdmin, system, tenant); + } + + /** Returns a {@link ProtoRole#tenantOperator} for the current system and given tenant. */ + public TenantRole tenantOperator(TenantName tenant) { + return new TenantRole(ProtoRole.tenantOperator, system, tenant); + } + + /** Returns a {@link ProtoRole#applicationOwner} for the current system and given tenant and application. */ + public ApplicationRole applicationOwner(TenantName tenant, ApplicationName application) { + return new ApplicationRole(ProtoRole.applicationOwner, system, tenant, application); + } + + /** Returns a {@link ProtoRole#applicationAdmin} for the current system and given tenant and application. */ + public ApplicationRole applicationAdmin(TenantName tenant, ApplicationName application) { + return new ApplicationRole(ProtoRole.applicationAdmin, system, tenant, application); + } + + /** Returns a {@link ProtoRole#applicationOperator} for the current system and given tenant and application. */ + public ApplicationRole applicationOperator(TenantName tenant, ApplicationName application) { + return new ApplicationRole(ProtoRole.applicationOperator, system, tenant, application); + } + + /** Returns a {@link ProtoRole#applicationDeveloper} for the current system and given tenant and application. */ + public ApplicationRole applicationDeveloper(TenantName tenant, ApplicationName application) { + return new ApplicationRole(ProtoRole.applicationDeveloper, system, tenant, application); + } + + /** Returns a {@link ProtoRole#applicationReader} for the current system and given tenant and application. */ + public ApplicationRole applicationReader(TenantName tenant, ApplicationName application) { + return new ApplicationRole(ProtoRole.applicationReader, system, tenant, application); + } + +} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/SecurityContext.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/SecurityContext.java index 1ad98e761f5..41444258a68 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/SecurityContext.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/SecurityContext.java @@ -3,24 +3,27 @@ package com.yahoo.vespa.hosted.controller.api.role; import java.security.Principal; import java.util.Objects; +import java.util.Set; + +import static java.util.Objects.requireNonNull; public class SecurityContext { public static final String ATTRIBUTE_NAME = SecurityContext.class.getName(); private final Principal principal; - private final RoleMembership roles; + private final Set roles; - public SecurityContext(Principal principal, RoleMembership roles) { - this.principal = principal; - this.roles = roles; + public SecurityContext(Principal principal, Set roles) { + this.principal = requireNonNull(principal); + this.roles = Set.copyOf(roles); } public Principal principal() { return principal; } - public RoleMembership roles() { + public Set roles() { return roles; } 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 new file mode 100644 index 00000000000..c3f8a5ef1b8 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/TenantRole.java @@ -0,0 +1,25 @@ +package com.yahoo.vespa.hosted.controller.api.role; + +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}. + * + * @author jonmv + */ +public class TenantRole extends Role { + + TenantRole(ProtoRole protoRole, SystemName system, TenantName tenant) { + super(protoRole, Context.limitedTo(tenant, system)); + } + + /** Returns the {@link TenantName} this is bound to. */ + public TenantName tenant() { return context.tenant().get(); } + + @Override + public String toString() { + return "role '" + proto() + "' of '" + tenant() + "'"; + } + +} 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 new file mode 100644 index 00000000000..ba01d949713 --- /dev/null +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/UnboundRole.java @@ -0,0 +1,21 @@ +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}. + * + * @author jonmv + */ +public class UnboundRole extends Role { + + UnboundRole(ProtoRole protoRole, SystemName system) { + super(protoRole, Context.unlimitedIn(system)); + } + + @Override + public String toString() { + return "role '" + proto() + "'"; + } + +} diff --git a/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleMembershipTest.java b/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleMembershipTest.java deleted file mode 100644 index c2eeb17c367..00000000000 --- a/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/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.api.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"))); - } - -} 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 new file mode 100644 index 00000000000..1badd157b1b --- /dev/null +++ b/controller-api/src/test/java/com/yahoo/vespa/hosted/controller/api/role/RoleTest.java @@ -0,0 +1,54 @@ +// 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.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 RoleTest { + + @Test + public void operator_membership() { + Role role = new Roles(SystemName.main).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"))); + } + + @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"))); + } + + @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"))); + } + +} 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 aabb658b7c1..f25deb11a52 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 @@ -2,7 +2,6 @@ package com.yahoo.vespa.hosted.controller.restapi.filter; 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.jdisc.Response; import com.yahoo.jdisc.http.filter.DiscFilterRequest; @@ -17,10 +16,10 @@ import com.yahoo.vespa.athenz.client.zms.ZmsClientException; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.TenantController; 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.SecurityContext; +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; import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant; import com.yahoo.vespa.hosted.controller.tenant.Tenant; import com.yahoo.vespa.hosted.controller.tenant.UserTenant; @@ -44,14 +43,14 @@ public class AthenzRoleFilter extends CorsRequestFilterBase { // TODO: No need f private final AthenzFacade athenz; private final TenantController tenants; - private final SystemName system; + private final Roles roles; @Inject public AthenzRoleFilter(CorsFilterConfig config, AthenzFacade athenz, Controller controller) { super(Set.copyOf(config.allowedUrls())); this.athenz = athenz; this.tenants = controller.tenants(); - this.system = controller.system(); + this.roles = new Roles(controller.system()); } @Override @@ -59,7 +58,7 @@ public class AthenzRoleFilter extends CorsRequestFilterBase { // TODO: No need f try { AthenzPrincipal athenzPrincipal = (AthenzPrincipal) request.getUserPrincipal(); request.setAttribute(SecurityContext.ATTRIBUTE_NAME, new SecurityContext(athenzPrincipal, - membership(athenzPrincipal, request.getUri()))); + roles(athenzPrincipal, request.getUri()))); return Optional.empty(); } catch (Exception e) { @@ -68,7 +67,7 @@ public class AthenzRoleFilter extends CorsRequestFilterBase { // TODO: No need f } } - RoleMembership membership(AthenzPrincipal principal, URI uri) { + Set roles(AthenzPrincipal principal, URI uri) { Path path = new Path(uri); path.matches("/application/v4/tenant/{tenant}/{*}"); @@ -80,18 +79,18 @@ public class AthenzRoleFilter extends CorsRequestFilterBase { // TODO: No need f AthenzIdentity identity = principal.getIdentity(); if (athenz.hasHostedOperatorAccess(identity)) - return Role.hostedOperator.limitedTo(system); + return Set.of(roles.hostedOperator()); if (tenant.isPresent() && isTenantAdmin(identity, tenant.get())) - return Role.athenzTenantAdmin.limitedTo(tenant.get().name(), system); + return Set.of(roles.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 Role.tenantPipeline.limitedTo(application.get(), tenant.get().name(), system); + return Set.of(roles.tenantPipeline(tenant.get().name(), application.get())); - return Role.everyone.limitedTo(system); + return Set.of(roles.everyone()); } private boolean isTenantAdmin(AthenzIdentity identity, Tenant tenant) { @@ -112,4 +111,5 @@ public class AthenzRoleFilter extends CorsRequestFilterBase { // TODO: No need f throw new RuntimeException("Failed to authorize operation: (" + e.getMessage() + ")", e); } } + } 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 a1a586b689d..39736d709d0 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 @@ -12,7 +12,7 @@ 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.Role; -import com.yahoo.vespa.hosted.controller.api.role.RoleMembership; +import com.yahoo.vespa.hosted.controller.api.role.Roles; import com.yahoo.vespa.hosted.controller.api.role.SecurityContext; import java.security.Principal; @@ -29,7 +29,7 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase { private static final Logger log = Logger.getLogger(ControllerAuthorizationFilter.class.getName()); - private final SystemName system; + private final Roles roles; @Inject public ControllerAuthorizationFilter(Controller controller, @@ -40,7 +40,7 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase { ControllerAuthorizationFilter(SystemName system, Set allowedUrls) { super(allowedUrls); - this.system = system; + this.roles = new Roles(system); } @Override @@ -54,12 +54,12 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase { Action action = Action.from(HttpRequest.Method.valueOf(request.getMethod())); - // Avoid expensive lookups when request is always legal. - if (Role.everyone.limitedTo(system).allows(action, request.getUri())) + // Avoid expensive look-ups when request is always legal. + if (roles.everyone().allows(action, request.getUri())) return Optional.empty(); - RoleMembership roles = securityContext.get().roles(); - if (roles.allows(action, request.getUri())) + Set roles = securityContext.get().roles(); + if (roles.stream().anyMatch(role -> role.allows(action, request.getUri()))) return Optional.empty(); } catch (Exception e) { 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 e862e12a18b..dc4235e52bf 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,16 +10,17 @@ 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.athenz.ApplicationAction; 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.api.role.Role; import org.junit.Before; import org.junit.Test; import java.net.URI; +import java.util.Set; import static org.junit.Assert.assertEquals; @@ -66,54 +67,56 @@ public class AthenzRoleFilterTest { @Test public void testTranslations() { + Roles roles = new Roles(tester.controller().system()); + // Hosted operators are always members of the hostedOperator role. - assertEquals(Role.hostedOperator.limitedTo(tester.controller().system()), - filter.membership(HOSTED_OPERATOR, NO_CONTEXT_PATH)); + assertEquals(Set.of(roles.hostedOperator()), + filter.roles(HOSTED_OPERATOR, NO_CONTEXT_PATH)); - assertEquals(Role.hostedOperator.limitedTo(tester.controller().system()), - filter.membership(HOSTED_OPERATOR, TENANT_CONTEXT_PATH)); + assertEquals(Set.of(roles.hostedOperator()), + filter.roles(HOSTED_OPERATOR, TENANT_CONTEXT_PATH)); - assertEquals(Role.hostedOperator.limitedTo(tester.controller().system()), - filter.membership(HOSTED_OPERATOR, APPLICATION_CONTEXT_PATH)); + assertEquals(Set.of(roles.hostedOperator()), + filter.roles(HOSTED_OPERATOR, APPLICATION_CONTEXT_PATH)); // Tenant admins are members of the athenzTenantAdmin role within their tenant subtree. - assertEquals(Role.everyone.limitedTo(tester.controller().system()), - filter.membership(TENANT_PIPELINE, NO_CONTEXT_PATH)); + assertEquals(Set.of(roles.everyone()), + filter.roles(TENANT_PIPELINE, NO_CONTEXT_PATH)); - assertEquals(Role.athenzTenantAdmin.limitedTo(TENANT, tester.controller().system()), - filter.membership(TENANT_ADMIN, TENANT_CONTEXT_PATH)); + assertEquals(Set.of(roles.athenzTenantAdmin(TENANT)), + filter.roles(TENANT_ADMIN, TENANT_CONTEXT_PATH)); - assertEquals(Role.athenzTenantAdmin.limitedTo(TENANT, tester.controller().system()), - filter.membership(TENANT_ADMIN, APPLICATION_CONTEXT_PATH)); + assertEquals(Set.of(roles.athenzTenantAdmin(TENANT)), + filter.roles(TENANT_ADMIN, APPLICATION_CONTEXT_PATH)); - assertEquals(Role.everyone.limitedTo(tester.controller().system()), - filter.membership(TENANT_ADMIN, TENANT2_CONTEXT_PATH)); + assertEquals(Set.of(roles.everyone()), + filter.roles(TENANT_ADMIN, TENANT2_CONTEXT_PATH)); - assertEquals(Role.everyone.limitedTo(tester.controller().system()), - filter.membership(TENANT_ADMIN, APPLICATION2_CONTEXT_PATH)); + assertEquals(Set.of(roles.everyone()), + filter.roles(TENANT_ADMIN, APPLICATION2_CONTEXT_PATH)); // Build services are members of the tenantPipeline role within their application subtree. - assertEquals(Role.everyone.limitedTo(tester.controller().system()), - filter.membership(TENANT_PIPELINE, NO_CONTEXT_PATH)); + assertEquals(Set.of(roles.everyone()), + filter.roles(TENANT_PIPELINE, NO_CONTEXT_PATH)); - assertEquals(Role.everyone.limitedTo(tester.controller().system()), - filter.membership(TENANT_PIPELINE, TENANT_CONTEXT_PATH)); + assertEquals(Set.of(roles.everyone()), + filter.roles(TENANT_PIPELINE, TENANT_CONTEXT_PATH)); - assertEquals(Role.tenantPipeline.limitedTo(APPLICATION, TENANT, tester.controller().system()), - filter.membership(TENANT_PIPELINE, APPLICATION_CONTEXT_PATH)); + assertEquals(Set.of(roles.tenantPipeline(TENANT, APPLICATION)), + filter.roles(TENANT_PIPELINE, APPLICATION_CONTEXT_PATH)); - assertEquals(Role.everyone.limitedTo(tester.controller().system()), - filter.membership(TENANT_PIPELINE, APPLICATION2_CONTEXT_PATH)); + assertEquals(Set.of(roles.everyone()), + filter.roles(TENANT_PIPELINE, APPLICATION2_CONTEXT_PATH)); // Unprivileged users are just members of the everyone role. - assertEquals(Role.everyone.limitedTo(tester.controller().system()), - filter.membership(USER, NO_CONTEXT_PATH)); + assertEquals(Set.of(roles.everyone()), + filter.roles(USER, NO_CONTEXT_PATH)); - assertEquals(Role.everyone.limitedTo(tester.controller().system()), - filter.membership(USER, TENANT_CONTEXT_PATH)); + assertEquals(Set.of(roles.everyone()), + filter.roles(USER, TENANT_CONTEXT_PATH)); - assertEquals(Role.everyone.limitedTo(tester.controller().system()), - filter.membership(USER, APPLICATION_CONTEXT_PATH)); + assertEquals(Set.of(roles.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 13c5d219878..105e10eefd2 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.Role; +import com.yahoo.vespa.hosted.controller.api.role.Roles; import com.yahoo.vespa.hosted.controller.api.role.SecurityContext; import com.yahoo.vespa.hosted.controller.restapi.ApplicationRequestToDiscFilterRequestWrapper; import org.junit.Test; @@ -34,8 +34,9 @@ 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())); ControllerAuthorizationFilter filter = createFilter(tester); - SecurityContext securityContext = new SecurityContext(() -> "operator", Role.hostedOperator.limitedTo(tester.controller().system())); assertIsAllowed(invokeFilter(filter, createRequest(Method.POST, "/zone/v2/path", securityContext))); assertIsAllowed(invokeFilter(filter, createRequest(Method.PUT, "/application/v4/user", securityContext))); @@ -45,9 +46,10 @@ public class ControllerAuthorizationFilterTest { @Test public void unprivileged() { ControllerTester tester = new ControllerTester(); - SecurityContext securityContext = new SecurityContext(() -> "user", Role.everyone.limitedTo(tester.controller().system())); - + Roles roles = new Roles(tester.controller().system()); + SecurityContext securityContext = new SecurityContext(() -> "user", Set.of(roles.everyone())); ControllerAuthorizationFilter filter = createFilter(tester); + assertIsForbidden(invokeFilter(filter, createRequest(Method.POST, "/zone/v2/path", securityContext))); assertIsAllowed(invokeFilter(filter, createRequest(Method.PUT, "/application/v4/user", securityContext))); assertIsAllowed(invokeFilter(filter, createRequest(Method.GET, "/zone/v1/path", securityContext))); @@ -57,8 +59,8 @@ public class ControllerAuthorizationFilterTest { public void unprivilegedInPublic() { ControllerTester tester = new ControllerTester(); tester.zoneRegistry().setSystemName(SystemName.Public); - - SecurityContext securityContext = new SecurityContext(() -> "user", Role.everyone.limitedTo(tester.controller().system())); + Roles roles = new Roles(tester.controller().system()); + SecurityContext securityContext = new SecurityContext(() -> "user", Set.of(roles.everyone())); ControllerAuthorizationFilter filter = createFilter(tester); assertIsForbidden(invokeFilter(filter, createRequest(Method.POST, "/zone/v2/path", securityContext))); @@ -112,4 +114,5 @@ public class ControllerAuthorizationFilterTest { this.message = message; } } + } -- cgit v1.2.3