diff options
author | Bjørn Christian Seime <bjorncs@oath.com> | 2018-02-20 13:39:25 +0100 |
---|---|---|
committer | Bjørn Christian Seime <bjorncs@oath.com> | 2018-02-21 11:15:21 +0100 |
commit | 46fe6cf4a37ac34b314fab8c1158ade51f1b37f4 (patch) | |
tree | 20a1f23391dd003b26bda16577dbc852d0915b4c /controller-server | |
parent | 59e254a7d8ea61ddfb2adde30b0aecef70913704 (diff) |
Remove dependency on Authorizer and ApplicationInstanceAuthorizer
Copy required authorization checks from Authorizer and
ApplicationInstanceAuthorizer.
Diffstat (limited to 'controller-server')
2 files changed, 78 insertions, 22 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) { 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 ff4ceae1c8e..9744b757c3d 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 @@ -148,7 +148,6 @@ public class ControllerAuthorizationFilterTest { return new ControllerAuthorizationFilter(new AthenzClientFactoryMock(controllerTester.athenzDb()), controllerTester.controller(), controllerTester.entityService(), - controllerTester.zoneRegistry(), new DefaultAuthorizationResponseHandler()); } |