summaryrefslogtreecommitdiffstats
path: root/controller-server/src/main/java/com/yahoo
diff options
context:
space:
mode:
authorBjørn Christian Seime <bjorncs@oath.com>2018-02-20 13:39:25 +0100
committerBjørn Christian Seime <bjorncs@oath.com>2018-02-22 09:08:32 +0100
commit04a2929f1074a0eeab69a33c8030870ceb8f418a (patch)
tree14e2995725a9c5ced52eaab0402e27429f06e21e /controller-server/src/main/java/com/yahoo
parentccfa114800406b3803a8cf5d480664b583a8b4ad (diff)
Remove dependency on Authorizer and ApplicationInstanceAuthorizer
Copy required authorization checks from Authorizer and ApplicationInstanceAuthorizer.
Diffstat (limited to 'controller-server/src/main/java/com/yahoo')
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java99
1 files changed, 78 insertions, 21 deletions
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 fddc2bb6fa1..d0d7164d874 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
@@ -8,16 +8,19 @@ import com.yahoo.jdisc.handler.ResponseHandler;
import com.yahoo.jdisc.http.HttpRequest.Method;
import com.yahoo.jdisc.http.filter.DiscFilterRequest;
import com.yahoo.jdisc.http.filter.SecurityRequestFilter;
+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.hosted.controller.Controller;
+import com.yahoo.vespa.hosted.controller.api.Tenant;
import com.yahoo.vespa.hosted.controller.api.identifiers.TenantId;
+import com.yahoo.vespa.hosted.controller.api.identifiers.UserId;
+import com.yahoo.vespa.hosted.controller.api.integration.athenz.ApplicationAction;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactory;
+import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsException;
import com.yahoo.vespa.hosted.controller.api.integration.entity.EntityService;
-import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry;
import com.yahoo.vespa.hosted.controller.restapi.Path;
-import com.yahoo.vespa.hosted.controller.restapi.application.ApplicationInstanceAuthorizer;
-import com.yahoo.vespa.hosted.controller.restapi.application.Authorizer;
import com.yahoo.yolean.chain.After;
import javax.ws.rs.ForbiddenException;
@@ -33,6 +36,7 @@ import static com.yahoo.jdisc.http.HttpRequest.Method.HEAD;
import static com.yahoo.jdisc.http.HttpRequest.Method.OPTIONS;
import static com.yahoo.jdisc.http.HttpRequest.Method.POST;
import static com.yahoo.jdisc.http.HttpRequest.Method.PUT;
+import static com.yahoo.vespa.hosted.controller.api.integration.athenz.HostedAthenzIdentities.SCREWDRIVER_DOMAIN;
import static com.yahoo.vespa.hosted.controller.restapi.filter.SecurityFilterUtils.sendErrorResponse;
/**
@@ -47,8 +51,7 @@ public class ControllerAuthorizationFilter implements SecurityRequestFilter {
private final AthenzClientFactory clientFactory;
private final Controller controller;
- private final Authorizer authorizer;
- private final ApplicationInstanceAuthorizer applicationInstanceAuthorizer;
+ private final EntityService entityService;
private final AuthorizationResponseHandler authorizationResponseHandler;
public interface AuthorizationResponseHandler {
@@ -58,20 +61,17 @@ public class ControllerAuthorizationFilter implements SecurityRequestFilter {
@Inject
public ControllerAuthorizationFilter(AthenzClientFactory clientFactory,
Controller controller,
- EntityService entityService,
- ZoneRegistry zoneRegistry) {
- this(clientFactory, controller, entityService, zoneRegistry, new DefaultAuthorizationResponseHandler());
+ EntityService entityService) {
+ this(clientFactory, controller, entityService, new DefaultAuthorizationResponseHandler());
}
ControllerAuthorizationFilter(AthenzClientFactory clientFactory,
Controller controller,
EntityService entityService,
- ZoneRegistry zoneRegistry,
AuthorizationResponseHandler authorizationResponseHandler) {
this.clientFactory = clientFactory;
this.controller = controller;
- this.authorizer = new Authorizer(controller, entityService, clientFactory);
- this.applicationInstanceAuthorizer = new ApplicationInstanceAuthorizer(zoneRegistry, clientFactory);
+ this.entityService = entityService;
this.authorizationResponseHandler = authorizationResponseHandler;
}
@@ -146,28 +146,85 @@ public class ControllerAuthorizationFilter implements SecurityRequestFilter {
}
}
+ private boolean isHostedOperator(AthenzIdentity identity) {
+ return clientFactory.createZmsClientWithServicePrincipal()
+ .hasHostedOperatorAccess(identity);
+ }
+
private void verifyIsTenantAdmin(AthenzPrincipal principal, TenantId tenantId) {
- if (!isTenantAdmin(principal.getIdentity(), tenantId)) {
+ boolean isTenantAdmin = controller.tenants().tenant(tenantId)
+ .map(tenant -> isTenantAdmin(principal.getIdentity(), tenant))
+ .orElse(false);
+ if (!isTenantAdmin) {
throw new ForbiddenException("Tenant admin or Vespa operator role required");
}
}
+ private boolean isTenantAdmin(AthenzIdentity identity, Tenant tenant) {
+ switch (tenant.tenantType()) {
+ case ATHENS:
+ return clientFactory.createZmsClientWithServicePrincipal()
+ .hasTenantAdminAccess(identity, tenant.getAthensDomain().get());
+ case OPSDB: {
+ if (!(identity instanceof AthenzUser)) {
+ return false;
+ }
+ AthenzUser user = (AthenzUser) identity;
+ return entityService.isGroupMember(new UserId(user.getName()), tenant.getUserGroup().get());
+ }
+ case USER: {
+ if (!(identity instanceof AthenzUser)) {
+ return false;
+ }
+ AthenzUser user = (AthenzUser) identity;
+ return tenant.getId().equals(new UserId(user.getName()).toTenantId());
+ }
+ default:
+ throw new InternalServerErrorException("Unknown tenant type: " + tenant.tenantType());
+ }
+ }
+
private void verifyIsTenantPipelineOperator(AthenzPrincipal principal,
TenantId tenantId,
- ApplicationName applicationName) {
+ ApplicationName application) {
controller.tenants().tenant(tenantId)
- .ifPresent(tenant -> applicationInstanceAuthorizer.throwIfUnauthorized(principal, tenant, applicationName));
+ .ifPresent(tenant -> verifyIsTenantPipelineOperator(principal.getIdentity(), tenant, application));
}
- private boolean isHostedOperator(AthenzIdentity identity) {
- return clientFactory.createZmsClientWithServicePrincipal()
- .hasHostedOperatorAccess(identity);
+ private void verifyIsTenantPipelineOperator(AthenzIdentity identity, Tenant tenant, ApplicationName application) {
+ if (isHostedOperator(identity)) return;
+
+ AthenzDomain principalDomain = identity.getDomain();
+ if (!principalDomain.equals(SCREWDRIVER_DOMAIN)) {
+ throw new ForbiddenException(String.format(
+ "'%s' is not a Screwdriver identity. Only Screwdriver is allowed to deploy to this environment.",
+ identity.getFullName()));
+ }
+
+ // NOTE: no fine-grained deploy authorization for non-Athenz tenants
+ if (tenant.isAthensTenant()) {
+ AthenzDomain tenantDomain = tenant.getAthensDomain().get();
+ if (!hasDeployerAccess(identity, tenantDomain, application)) {
+ throw new ForbiddenException(String.format(
+ "'%1$s' does not have access to '%2$s'. " +
+ "Either the application has not been created at Vespa dashboard or " +
+ "'%1$s' is not added to the application's deployer role in Athenz domain '%3$s'.",
+ identity.getFullName(), application.value(), tenantDomain.getName()));
+ }
+ }
}
- private boolean isTenantAdmin(AthenzIdentity identity, TenantId tenantId) {
- return controller.tenants().tenant(tenantId)
- .map(tenant -> authorizer.isTenantAdmin(identity, tenant))
- .orElse(false);
+ private boolean hasDeployerAccess(AthenzIdentity identity, AthenzDomain tenantDomain, ApplicationName application) {
+ try {
+ return clientFactory.createZmsClientWithServicePrincipal()
+ .hasApplicationAccess(
+ identity,
+ ApplicationAction.deploy,
+ tenantDomain,
+ new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId(application.value()));
+ } catch (ZmsException e) {
+ throw new InternalServerErrorException("Failed to authorize operation: (" + e.getMessage() + ")", e);
+ }
}
private static TenantId getTenantId(Path path) {