summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorJon Marius Venstad <jvenstad@yahoo-inc.com>2019-03-27 12:22:18 +0100
committerJon Marius Venstad <jvenstad@yahoo-inc.com>2019-03-28 10:24:34 +0100
commit3fcace39ee506c40545edade06245b7c1127c71b (patch)
treed2e9dbf9c91f265b99c811a8f007ca7b1117ef36 /controller-server
parent64d9c30b34dc66e85e3a140540b65532d3446d96 (diff)
Move AthenzRoleResolver to upper level
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleResolver.java111
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java128
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java1
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java4
4 files changed, 122 insertions, 122 deletions
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleResolver.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleResolver.java
new file mode 100644
index 00000000000..38d827a78ad
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/AthenzRoleResolver.java
@@ -0,0 +1,111 @@
+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.restapi.Path;
+import com.yahoo.vespa.athenz.api.AthenzDomain;
+import com.yahoo.vespa.athenz.api.AthenzIdentity;
+import com.yahoo.vespa.athenz.api.AthenzPrincipal;
+import com.yahoo.vespa.athenz.api.AthenzUser;
+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.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.tenant.AthenzTenant;
+import com.yahoo.vespa.hosted.controller.tenant.Tenant;
+import com.yahoo.vespa.hosted.controller.tenant.UserTenant;
+
+import javax.ws.rs.InternalServerErrorException;
+import java.security.Principal;
+import java.util.Optional;
+
+import static com.yahoo.vespa.hosted.controller.athenz.HostedAthenzIdentities.SCREWDRIVER_DOMAIN;
+
+public class AthenzRoleResolver implements RoleMembership.Resolver {
+
+ private final AthenzFacade athenz;
+ private final TenantController tenants;
+ private final SystemName system;
+
+ @Inject
+ public AthenzRoleResolver(AthenzFacade athenz, Controller controller) {
+ this.athenz = athenz;
+ this.tenants = controller.tenants();
+ this.system = controller.system();
+ }
+
+ private boolean isTenantAdmin(AthenzIdentity identity, Tenant tenant) {
+ if (tenant instanceof AthenzTenant) {
+ return athenz.hasTenantAdminAccess(identity, ((AthenzTenant) tenant).domain());
+ } else if (tenant instanceof UserTenant) {
+ if (!(identity instanceof AthenzUser)) {
+ return false;
+ }
+ AthenzUser user = (AthenzUser) identity;
+ return ((UserTenant) tenant).is(user.getName()) || isHostedOperator(identity);
+ }
+ throw new InternalServerErrorException("Unknown tenant type: " + tenant.getClass().getSimpleName());
+ }
+
+ private boolean hasDeployerAccess(AthenzIdentity identity, AthenzDomain tenantDomain, ApplicationName application) {
+ try {
+ return athenz.hasApplicationAccess(identity,
+ ApplicationAction.deploy,
+ tenantDomain,
+ application);
+ } catch (ZmsClientException e) {
+ throw new InternalServerErrorException("Failed to authorize operation: (" + e.getMessage() + ")", e);
+ }
+ }
+
+ private boolean isHostedOperator(AthenzIdentity identity) {
+ return athenz.hasHostedOperatorAccess(identity);
+ }
+
+ @Override
+ public RoleMembership membership(Principal principal, Optional<String> uriPath) {
+ if ( ! (principal instanceof AthenzPrincipal))
+ throw new IllegalStateException("Expected an AthenzPrincipal to be set on the request.");
+
+ Path path = new Path(uriPath.orElseThrow(() -> new IllegalArgumentException("This resolver needs the request path.")));
+
+ path.matches("/application/v4/tenant/{tenant}/{*}");
+ Optional<Tenant> tenant = Optional.ofNullable(path.get("tenant")).map(TenantName::from).flatMap(tenants::get);
+
+ path.matches("/application/v4/tenant/{tenant}/application/{application}/{*}");
+ Optional<ApplicationName> application = Optional.ofNullable(path.get("application")).map(ApplicationName::from);
+
+ AthenzIdentity identity = ((AthenzPrincipal) principal).getIdentity();
+
+ RoleMembership.Builder memberships = RoleMembership.in(system);
+ if (isHostedOperator(identity)) {
+ memberships.add(Role.hostedOperator);
+ }
+ if (tenant.isPresent() && isTenantAdmin(identity, tenant.get())) {
+ memberships.add(Role.tenantAdmin).limitedTo(tenant.get().name());
+ }
+ AthenzDomain principalDomain = identity.getDomain();
+ if (principalDomain.equals(SCREWDRIVER_DOMAIN)) {
+ if (application.isPresent() && tenant.isPresent()) {
+ // NOTE: Only fine-grained deploy authorization for Athenz tenants
+ if (tenant.get() instanceof AthenzTenant) {
+ AthenzDomain tenantDomain = ((AthenzTenant) tenant.get()).domain();
+ if (hasDeployerAccess(identity, tenantDomain, application.get())) {
+ memberships.add(Role.tenantPipelineOperator).limitedTo(tenant.get().name(), application.get());
+ }
+ }
+ else {
+ memberships.add(Role.tenantPipelineOperator).limitedTo(tenant.get().name(), application.get());
+ }
+ }
+ }
+ memberships.add(Role.everyone);
+ return memberships.build();
+ }
+
+}
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 717704b9736..3e604732f52 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
@@ -2,48 +2,24 @@
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.HttpRequest;
import com.yahoo.jdisc.http.filter.DiscFilterRequest;
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.restapi.Path;
-import com.yahoo.vespa.athenz.api.AthenzDomain;
-import com.yahoo.vespa.athenz.api.AthenzIdentity;
-import com.yahoo.vespa.athenz.api.AthenzPrincipal;
-import com.yahoo.vespa.athenz.api.AthenzUser;
-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.integration.athenz.AthenzClientFactory;
-import com.yahoo.vespa.hosted.controller.athenz.ApplicationAction;
-import com.yahoo.vespa.hosted.controller.athenz.impl.AthenzFacade;
import com.yahoo.vespa.hosted.controller.role.Action;
-import com.yahoo.vespa.hosted.controller.role.Context;
-import com.yahoo.vespa.hosted.controller.role.Role;
import com.yahoo.vespa.hosted.controller.role.RoleMembership;
-import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant;
-import com.yahoo.vespa.hosted.controller.tenant.Tenant;
-import com.yahoo.vespa.hosted.controller.tenant.UserTenant;
import com.yahoo.yolean.chain.After;
import com.yahoo.yolean.chain.Provides;
-import javax.ws.rs.ForbiddenException;
-import javax.ws.rs.InternalServerErrorException;
import javax.ws.rs.WebApplicationException;
import java.security.Principal;
-import java.util.HashMap;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Logger;
-import static com.yahoo.vespa.hosted.controller.athenz.HostedAthenzIdentities.SCREWDRIVER_DOMAIN;
-
/**
* A security filter protects all controller apis.
*
@@ -55,21 +31,21 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase {
private static final Logger log = Logger.getLogger(ControllerAuthorizationFilter.class.getName());
- private final AthenzFacade athenz;
+ private final RoleMembership.Resolver roleResolver;
private final Controller controller;
@Inject
- public ControllerAuthorizationFilter(AthenzClientFactory clientFactory,
+ public ControllerAuthorizationFilter(RoleMembership.Resolver roleResolver,
Controller controller,
CorsFilterConfig corsConfig) {
- this(clientFactory, controller, Set.copyOf(corsConfig.allowedUrls()));
+ this(roleResolver, controller, Set.copyOf(corsConfig.allowedUrls()));
}
- ControllerAuthorizationFilter(AthenzClientFactory clientFactory,
+ ControllerAuthorizationFilter(RoleMembership.Resolver roleResolver,
Controller controller,
Set<String> allowedUrls) {
super(allowedUrls);
- this.athenz = new AthenzFacade(clientFactory);;
+ this.roleResolver = roleResolver;
this.controller = controller;
}
@@ -86,14 +62,13 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase {
if (RoleMembership.everyoneIn(controller.system()).allows(action, request.getRequestURI()))
return Optional.empty();
- RoleMembership roles = new AthenzRoleResolver(athenz, controller).membership(principal,
- Optional.of(request.getRequestURI()));
+ RoleMembership roles = this.roleResolver.membership(principal, Optional.of(request.getRequestURI()));
if (roles.allows(action, request.getRequestURI()))
return Optional.empty();
return Optional.of(new ErrorResponse(Response.Status.FORBIDDEN, "Access denied"));
}
- catch (WebApplicationException e) {
+ catch (WebApplicationException e) { // TODO move to resolver.
int statusCode = e.getResponse().getStatus();
String errorMessage = e.getMessage();
log.log(LogLevel.WARNING, String.format("Access denied (%d): %s", statusCode, errorMessage));
@@ -101,93 +76,4 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase {
}
}
- // TODO: Pull class up and resolve roles from roles defined in Athenz
- private static class AthenzRoleResolver implements RoleMembership.Resolver {
-
- private final AthenzFacade athenz;
- private final TenantController tenants;
- private final SystemName system;
-
- public AthenzRoleResolver(AthenzFacade athenz, Controller controller) {
- this.athenz = athenz;
- this.tenants = controller.tenants();
- this.system = controller.system();
- }
-
- private boolean isTenantAdmin(AthenzIdentity identity, Tenant tenant) {
- if (tenant instanceof AthenzTenant) {
- return athenz.hasTenantAdminAccess(identity, ((AthenzTenant) tenant).domain());
- } else if (tenant instanceof UserTenant) {
- if (!(identity instanceof AthenzUser)) {
- return false;
- }
- AthenzUser user = (AthenzUser) identity;
- return ((UserTenant) tenant).is(user.getName()) || isHostedOperator(identity);
- }
- throw new InternalServerErrorException("Unknown tenant type: " + tenant.getClass().getSimpleName());
- }
-
- private boolean hasDeployerAccess(AthenzIdentity identity, AthenzDomain tenantDomain, ApplicationName application) {
- try {
- return athenz.hasApplicationAccess(identity,
- ApplicationAction.deploy,
- tenantDomain,
- application);
- } catch (ZmsClientException e) {
- throw new InternalServerErrorException("Failed to authorize operation: (" + e.getMessage() + ")", e);
- }
- }
-
- private boolean isHostedOperator(AthenzIdentity identity) {
- return athenz.hasHostedOperatorAccess(identity);
- }
-
- @Override
- public RoleMembership membership(Principal principal, Optional<String> uriPath) {
- if ( ! (principal instanceof AthenzPrincipal))
- throw new IllegalStateException("Expected an AthenzPrincipal to be set on the request.");
-
- Path path = new Path(uriPath.orElseThrow(() -> new IllegalArgumentException("This resolver needs the request path.")));
-
- path.matches("/application/v4/tenant/{tenant}/{*}");
- Optional<Tenant> tenant = Optional.ofNullable(path.get("tenant")).map(TenantName::from).flatMap(tenants::get);
-
- path.matches("/application/v4/tenant/{tenant}/application/{application}/{*}");
- Optional<ApplicationName> application = Optional.ofNullable(path.get("application")).map(ApplicationName::from);
-
- AthenzIdentity identity = ((AthenzPrincipal) principal).getIdentity();
-
- RoleMembership.Builder memberships = RoleMembership.in(system);
- if (isHostedOperator(identity)) {
- memberships.add(Role.hostedOperator);
- }
- if (tenant.isPresent() && isTenantAdmin(identity, tenant.get())) {
- memberships.add(Role.tenantAdmin).limitedTo(tenant.get().name());
- }
- AthenzDomain principalDomain = identity.getDomain();
- if (principalDomain.equals(SCREWDRIVER_DOMAIN)) {
- if (application.isPresent() && tenant.isPresent()) {
- // NOTE: Only fine-grained deploy authorization for Athenz tenants
- if (tenant.get() instanceof AthenzTenant) {
- AthenzDomain tenantDomain = ((AthenzTenant) tenant.get()).domain();
- if (hasDeployerAccess(identity, tenantDomain, application.get())) {
- memberships.add(Role.tenantPipelineOperator).limitedTo(tenant.get().name(), application.get());
- }
- }
- else {
- memberships.add(Role.tenantPipelineOperator).limitedTo(tenant.get().name(), application.get());
- }
- }
- }
- memberships.add(Role.everyone);
- return memberships.build();
- }
-
- }
-
- private static Optional<AthenzPrincipal> principalFrom(DiscFilterRequest request) {
- return Optional.ofNullable(request.getUserPrincipal())
- .map(AthenzPrincipal.class::cast);
- }
-
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java
index ab19ccf23d2..71b65770b1e 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java
@@ -95,6 +95,7 @@ public class ControllerContainerTest {
" <component id='com.yahoo.vespa.hosted.controller.api.integration.stubs.MockMailer'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.security.AthenzAccessControlRequests'/>\n" +
" <component id='com.yahoo.vespa.hosted.controller.athenz.impl.AthenzFacade'/>\n" +
+ " <component id='com.yahoo.vespa.hosted.controller.restapi.filter.AthenzRoleResolver'/>\n" +
" <handler id='com.yahoo.vespa.hosted.controller.restapi.application.ApplicationApiHandler'>\n" +
" <binding>http://*/application/v4/*</binding>\n" +
" </handler>\n" +
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 e94526c7dd9..0a050b8d50d 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
@@ -16,6 +16,7 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId;
import com.yahoo.vespa.hosted.controller.api.identifiers.ScrewdriverId;
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.restapi.ApplicationRequestToDiscFilterRequestWrapper;
@@ -170,7 +171,8 @@ public class ControllerAuthorizationFilterTest {
}
private static ControllerAuthorizationFilter createFilter(ControllerTester controllerTester) {
- return new ControllerAuthorizationFilter(new AthenzClientFactoryMock(controllerTester.athenzDb()),
+ return new ControllerAuthorizationFilter(new AthenzRoleResolver(new AthenzFacade(new AthenzClientFactoryMock(controllerTester.athenzDb())),
+ controllerTester.controller()),
controllerTester.controller(),
Set.of("http://localhost"));
}