From 906980b5722c061673574fa74d6960e077f477c1 Mon Sep 17 00:00:00 2001 From: Bjørn Christian Seime Date: Wed, 24 Oct 2018 13:27:05 +0200 Subject: Create Athenz tenancy using Okta - Use ZmsClient from vespa-athenz in AthenzClientFactory - Rename ZmsClientImpl (controller-server) to ZmsClientFacade and modify it to use ZmsClient - Move ApplicationAction and HostedAthenzIdentities to controller-server - Change ZmsClientMock to mock ZmsClient from vespa-athenz --- .../api/integration/athenz/ApplicationAction.java | 17 -- .../integration/athenz/AthenzClientFactory.java | 13 +- .../integration/athenz/HostedAthenzIdentities.java | 27 --- .../api/integration/athenz/ZmsClient.java | 34 ---- .../api/integration/athenz/ZmsException.java | 24 --- controller-server/pom.xml | 44 ----- .../hosted/controller/ApplicationController.java | 24 ++- .../yahoo/vespa/hosted/controller/Controller.java | 7 +- .../vespa/hosted/controller/TenantController.java | 36 ++-- .../controller/athenz/ApplicationAction.java | 17 ++ .../controller/athenz/HostedAthenzIdentities.java | 27 +++ .../athenz/impl/AthenzClientFactoryImpl.java | 64 +------ .../controller/athenz/impl/ZmsClientFacade.java | 122 +++++++++++++ .../controller/athenz/impl/ZmsClientImpl.java | 191 --------------------- .../athenz/mock/AthenzClientFactoryMock.java | 20 +-- .../controller/athenz/mock/AthenzDbMock.java | 2 +- .../controller/athenz/mock/ZmsClientMock.java | 117 ++++++++----- .../restapi/application/ApplicationApiHandler.java | 37 ++-- .../filter/ControllerAuthorizationFilter.java | 25 ++- .../vespa/hosted/controller/ControllerTest.java | 4 +- .../vespa/hosted/controller/ControllerTester.java | 5 +- .../vespa/hosted/controller/TestIdentities.java | 25 --- .../controller/integration/AthenzFilterMock.java | 8 +- .../controller/maintenance/DnsMaintainerTest.java | 4 +- .../controller/maintenance/JobRunnerTest.java | 6 +- .../restapi/ContainerControllerTester.java | 10 +- .../restapi/ControllerContainerTest.java | 7 +- .../restapi/application/ApplicationApiTest.java | 74 ++++---- .../filter/ControllerAuthorizationFilterTest.java | 4 +- 29 files changed, 386 insertions(+), 609 deletions(-) delete mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ApplicationAction.java delete mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/HostedAthenzIdentities.java delete mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClient.java delete mode 100644 controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsException.java create mode 100644 controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/ApplicationAction.java create mode 100644 controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/HostedAthenzIdentities.java create mode 100644 controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsClientFacade.java delete mode 100644 controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsClientImpl.java delete mode 100644 controller-server/src/test/java/com/yahoo/vespa/hosted/controller/TestIdentities.java diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ApplicationAction.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ApplicationAction.java deleted file mode 100644 index 3323cda89b3..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ApplicationAction.java +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.api.integration.athenz; - -/** - * @author bjorncs - */ -public enum ApplicationAction { - deploy("deployer"), - read("reader"), - write("writer"); - - public final String roleName; - - ApplicationAction(String roleName) { - this.roleName = roleName; - } -} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzClientFactory.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzClientFactory.java index e95e97527da..aba3b5f3ab7 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzClientFactory.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/AthenzClientFactory.java @@ -1,8 +1,8 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.api.integration.athenz; -import com.yahoo.vespa.athenz.api.AthenzIdentity; -import com.yahoo.vespa.athenz.api.NToken; +import com.yahoo.vespa.athenz.api.AthenzService; +import com.yahoo.vespa.athenz.client.zms.ZmsClient; import com.yahoo.vespa.athenz.client.zts.ZtsClient; /** @@ -10,12 +10,9 @@ import com.yahoo.vespa.athenz.client.zts.ZtsClient; */ public interface AthenzClientFactory { - AthenzIdentity getControllerIdentity(); + AthenzService getControllerIdentity(); - ZmsClient createZmsClientWithServicePrincipal(); - - ZtsClient createZtsClientWithServicePrincipal(); - - ZmsClient createZmsClientWithAuthorizedServiceToken(NToken authorizedServiceToken); + ZmsClient createZmsClient(); + ZtsClient createZtsClient(); } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/HostedAthenzIdentities.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/HostedAthenzIdentities.java deleted file mode 100644 index bd385034a90..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/HostedAthenzIdentities.java +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.api.integration.athenz; - -import com.yahoo.vespa.athenz.api.AthenzDomain; -import com.yahoo.vespa.athenz.api.AthenzService; -import com.yahoo.vespa.athenz.api.AthenzUser; -import com.yahoo.vespa.hosted.controller.api.identifiers.ScrewdriverId; -import com.yahoo.vespa.hosted.controller.api.identifiers.UserId; - -/** - * @author bjorncs - */ -public class HostedAthenzIdentities { - - public static final AthenzDomain SCREWDRIVER_DOMAIN = new AthenzDomain("cd.screwdriver.project"); - - private HostedAthenzIdentities() {} - - public static AthenzUser from(UserId userId) { - return AthenzUser.fromUserId(userId.id()); - } - - public static AthenzService from(ScrewdriverId screwdriverId) { - return new AthenzService(SCREWDRIVER_DOMAIN, "sd" + screwdriverId.id()); - } - -} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClient.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClient.java deleted file mode 100644 index 3630748b10a..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClient.java +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.api.integration.athenz; - -import com.yahoo.vespa.athenz.api.AthenzDomain; -import com.yahoo.vespa.athenz.api.AthenzIdentity; -import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId; - -import java.util.List; - -/** - * @author bjorncs - */ -public interface ZmsClient { - - void createTenant(AthenzDomain tenantDomain); - - void deleteTenant(AthenzDomain tenantDomain); - - void addApplication(AthenzDomain tenantDomain, ApplicationId applicationName); - - void deleteApplication(AthenzDomain tenantDomain, ApplicationId applicationName); - - boolean hasApplicationAccess(AthenzIdentity athenzIdentity, ApplicationAction action, AthenzDomain tenantDomain, ApplicationId applicationName); - - boolean hasTenantAdminAccess(AthenzIdentity athenzIdentity, AthenzDomain tenantDomain); - - boolean hasHostedOperatorAccess(AthenzIdentity identity); - - // Used before vespa tenancy is established for the domain. - boolean isDomainAdmin(AthenzIdentity athenzIdentity, AthenzDomain domain); - - List getDomainList(String prefix); - -} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsException.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsException.java deleted file mode 100644 index 31e9e549c08..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsException.java +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.api.integration.athenz; - -/** - * @author bjorncs - */ -public class ZmsException extends RuntimeException { - - private final int code; - - public ZmsException(int code, Throwable cause) { - super(cause.getMessage(), cause); - this.code = code; - } - - public ZmsException(int code, String message) { - super(message); - this.code = code; - } - - public int getCode() { - return code; - } -} diff --git a/controller-server/pom.xml b/controller-server/pom.xml index 44954e19fea..b15a561c1d2 100644 --- a/controller-server/pom.xml +++ b/controller-server/pom.xml @@ -130,50 +130,6 @@ 1.6 - - com.yahoo.athenz - athenz-zms-java-client - compile - - - org.slf4j - slf4j-api - - - - org.glassfish.jersey.core - jersey-client - - - org.glassfish.jersey.media - jersey-media-json-jackson - - - - org.bouncycastle - bcpkix-jdk15on - - - org.bouncycastle - bcprov-jdk15on - - - - com.fasterxml.jackson.core - jackson-core - - - com.fasterxml.jackson.core - jackson-databind - - - com.fasterxml.jackson.core - jackson-annotations - - - - diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java index 79bc72f950d..7e15b780df4 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java @@ -9,7 +9,7 @@ import com.yahoo.config.application.api.ValidationOverrides; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.TenantName; -import com.yahoo.vespa.athenz.api.NToken; +import com.yahoo.vespa.athenz.api.OktaAccessToken; import com.yahoo.vespa.curator.Lock; import com.yahoo.vespa.hosted.controller.api.ActivateResult; import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions; @@ -20,7 +20,6 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.Hostname; import com.yahoo.vespa.hosted.controller.api.identifiers.RevisionId; import com.yahoo.vespa.hosted.controller.api.integration.BuildService; import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactory; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsClient; import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServer; import com.yahoo.vespa.hosted.controller.api.integration.configserver.Log; import com.yahoo.vespa.hosted.controller.api.integration.configserver.NoInstanceException; @@ -43,6 +42,7 @@ import com.yahoo.vespa.hosted.controller.application.JobList; import com.yahoo.vespa.hosted.controller.application.JobStatus; import com.yahoo.vespa.hosted.controller.application.JobStatus.JobRun; import com.yahoo.vespa.hosted.controller.application.SystemApplication; +import com.yahoo.vespa.hosted.controller.athenz.impl.ZmsClientFacade; import com.yahoo.vespa.hosted.controller.concurrent.Once; import com.yahoo.vespa.hosted.controller.deployment.DeploymentSteps; import com.yahoo.vespa.hosted.controller.deployment.DeploymentTrigger; @@ -93,7 +93,7 @@ public class ApplicationController { private final ArtifactRepository artifactRepository; private final ApplicationStore applicationStore; private final RotationRepository rotationRepository; - private final AthenzClientFactory zmsClientFactory; + private final ZmsClientFacade zmsClient; private final NameService nameService; private final ConfigServer configServer; private final RoutingGenerator routingGenerator; @@ -108,7 +108,7 @@ public class ApplicationController { RoutingGenerator routingGenerator, BuildService buildService, Clock clock) { this.controller = controller; this.curator = curator; - this.zmsClientFactory = zmsClientFactory; + this.zmsClient = new ZmsClientFacade(zmsClientFactory.createZmsClient(), zmsClientFactory.getControllerIdentity()); this.nameService = nameService; this.configServer = configServer; this.routingGenerator = routingGenerator; @@ -250,7 +250,7 @@ public class ApplicationController { * * @throws IllegalArgumentException if the application already exists */ - public Application createApplication(ApplicationId id, Optional token) { + public Application createApplication(ApplicationId id, Optional token) { if ( ! (id.instance().isDefault())) // TODO: Support instances properly throw new IllegalArgumentException("Only the instance name 'default' is supported at the moment"); if (id.instance().isTester()) @@ -269,11 +269,10 @@ public class ApplicationController { throw new IllegalArgumentException("Could not create '" + id + "': Application " + dashToUnderscore(id) + " already exists"); if (id.instance().isDefault() && tenant.get() instanceof AthenzTenant) { // Only create the athenz application for "default" instances. if ( ! token.isPresent()) - throw new IllegalArgumentException("Could not create '" + id + "': No NToken provided"); + throw new IllegalArgumentException("Could not create '" + id + "': No Okta Access Token provided"); - ZmsClient zmsClient = zmsClientFactory.createZmsClientWithAuthorizedServiceToken(token.get()); zmsClient.addApplication(((AthenzTenant) tenant.get()).domain(), - new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId(id.application().value())); + new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId(id.application().value()), token.get()); } LockedApplication application = new LockedApplication(new Application(id, clock.instant()), lock); store(application); @@ -531,7 +530,7 @@ public class ApplicationController { * @throws IllegalArgumentException if the application has deployments or the caller is not authorized * @throws NotExistsException if no instances of the application exist */ - public void deleteApplication(ApplicationId applicationId, Optional token) { + public void deleteApplication(ApplicationId applicationId, Optional token) { // Find all instances of the application List instances = asList(applicationId.tenant()).stream() .map(Application::id) @@ -548,13 +547,12 @@ public class ApplicationController { Tenant tenant = controller.tenants().tenant(id.tenant()).get(); if (tenant instanceof AthenzTenant && ! token.isPresent()) - throw new IllegalArgumentException("Could not delete '" + application + "': No NToken provided"); + throw new IllegalArgumentException("Could not delete '" + application + "': No Okta Access Token provided"); // Only delete in Athenz once if (id.instance().isDefault() && tenant instanceof AthenzTenant) { - zmsClientFactory.createZmsClientWithAuthorizedServiceToken(token.get()) - .deleteApplication(((AthenzTenant) tenant).domain(), - new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId(id.application().value())); + zmsClient.deleteApplication(((AthenzTenant) tenant).domain(), + new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId(id.application().value()), token.get()); } curator.removeApplication(id); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java index 9e3c5a2d683..47e11af056d 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java @@ -26,6 +26,7 @@ import com.yahoo.vespa.hosted.controller.api.integration.github.GitHub; import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingGenerator; import com.yahoo.vespa.hosted.controller.api.integration.zone.CloudName; import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneRegistry; +import com.yahoo.vespa.hosted.controller.athenz.impl.ZmsClientFacade; import com.yahoo.vespa.hosted.controller.deployment.JobController; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; import com.yahoo.vespa.hosted.controller.versions.OsVersion; @@ -74,7 +75,7 @@ public class Controller extends AbstractComponent { private final ConfigServer configServer; private final MetricsService metricsService; private final Chef chef; - private final AthenzClientFactory athenzClientFactory; + private final ZmsClientFacade zmsClient; /** * Creates a controller @@ -114,7 +115,7 @@ public class Controller extends AbstractComponent { this.metricsService = Objects.requireNonNull(metricsService, "MetricsService cannot be null"); this.chef = Objects.requireNonNull(chef, "Chef cannot be null"); this.clock = Objects.requireNonNull(clock, "Clock cannot be null"); - this.athenzClientFactory = Objects.requireNonNull(athenzClientFactory, "AthenzClientFactory cannot be null"); + this.zmsClient = new ZmsClientFacade(athenzClientFactory.createZmsClient(), athenzClientFactory.getControllerIdentity()); jobController = new JobController(this, runDataStore, Objects.requireNonNull(testerCloud)); applicationController = new ApplicationController(this, curator, athenzClientFactory, @@ -144,7 +145,7 @@ public class Controller extends AbstractComponent { public JobController jobController() { return jobController; } public List getDomainList(String prefix) { - return athenzClientFactory.createZmsClientWithServicePrincipal().getDomainList(prefix); + return zmsClient.getDomainList(prefix); } /** diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/TenantController.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/TenantController.java index 1ae3e6a6577..f0e13349fbf 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/TenantController.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/TenantController.java @@ -3,13 +3,14 @@ package com.yahoo.vespa.hosted.controller; import com.yahoo.config.provision.TenantName; import com.yahoo.vespa.athenz.api.AthenzDomain; +import com.yahoo.vespa.athenz.api.AthenzService; import com.yahoo.vespa.athenz.api.AthenzUser; -import com.yahoo.vespa.athenz.api.NToken; +import com.yahoo.vespa.athenz.api.OktaAccessToken; import com.yahoo.vespa.athenz.client.zts.ZtsClient; import com.yahoo.vespa.curator.Lock; import com.yahoo.vespa.hosted.controller.api.identifiers.UserId; import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactory; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsClient; +import com.yahoo.vespa.hosted.controller.athenz.impl.ZmsClientFacade; import com.yahoo.vespa.hosted.controller.concurrent.Once; import com.yahoo.vespa.hosted.controller.persistence.CuratorDb; import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant; @@ -41,12 +42,16 @@ public class TenantController { private final Controller controller; private final CuratorDb curator; - private final AthenzClientFactory athenzClientFactory; + private final ZmsClientFacade zmsClient; + private final ZtsClient ztsClient; + private final AthenzService controllerIdentity; public TenantController(Controller controller, CuratorDb curator, AthenzClientFactory athenzClientFactory) { this.controller = Objects.requireNonNull(controller, "controller must be non-null"); this.curator = Objects.requireNonNull(curator, "curator must be non-null"); - this.athenzClientFactory = Objects.requireNonNull(athenzClientFactory, "athenzClientFactory must be non-null"); + this.controllerIdentity = athenzClientFactory.getControllerIdentity(); + this.zmsClient = new ZmsClientFacade(athenzClientFactory.createZmsClient(), controllerIdentity); + this.ztsClient = athenzClientFactory.createZtsClient(); // Update serialization format of all tenants Once.after(Duration.ofMinutes(1), () -> { @@ -84,13 +89,11 @@ public class TenantController { /** Returns a list of all tenants accessible by the given user */ public List asList(UserId user) { AthenzUser athenzUser = AthenzUser.fromUserId(user.id()); - try (ZtsClient ztsClient = athenzClientFactory.createZtsClientWithServicePrincipal()) { - Set userDomains = new HashSet<>(ztsClient.getTenantDomains(athenzClientFactory.getControllerIdentity(), athenzUser, "admin")); + Set userDomains = new HashSet<>(ztsClient.getTenantDomains(controllerIdentity, athenzUser, "admin")); return asList().stream() .filter(tenant -> isUser(tenant, user) || userDomains.stream().anyMatch(domain -> inDomain(tenant, domain))) .collect(Collectors.toList()); - } } /** @@ -124,7 +127,7 @@ public class TenantController { } /** Create an Athenz tenant */ - public void create(AthenzTenant tenant, NToken token) { + public void create(AthenzTenant tenant, OktaAccessToken token) { try (Lock lock = lock(tenant.name())) { requireNonExistent(tenant.name()); AthenzDomain domain = tenant.domain(); @@ -136,7 +139,7 @@ public class TenantController { existingTenantWithDomain.get().name().value() + "'"); } - athenzClientFactory.createZmsClientWithAuthorizedServiceToken(token).createTenant(domain); + zmsClient.createTenant(domain, token); curator.writeTenant(tenant); } } @@ -169,7 +172,7 @@ public class TenantController { } /** Update Athenz domain for tenant. Returns the updated tenant which must be explicitly stored */ - public LockedTenant withDomain(LockedTenant tenant, AthenzDomain newDomain, NToken token) { + public LockedTenant withDomain(LockedTenant tenant, AthenzDomain newDomain, OktaAccessToken token) { AthenzDomain existingDomain = tenant.get().domain(); if (existingDomain.equals(newDomain)) return tenant; Optional existingTenantWithNewDomain = tenantIn(newDomain); @@ -177,12 +180,11 @@ public class TenantController { throw new IllegalArgumentException("Could not set domain of " + tenant + " to '" + newDomain + "':" + existingTenantWithNewDomain.get() + " already has this domain"); - ZmsClient zmsClient = athenzClientFactory.createZmsClientWithAuthorizedServiceToken(token); - zmsClient.createTenant(newDomain); + zmsClient.createTenant(newDomain, token); List applications = controller.applications().asList(tenant.get().name()); - applications.forEach(a -> zmsClient.addApplication(newDomain, new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId(a.id().application().value()))); - applications.forEach(a -> zmsClient.deleteApplication(existingDomain, new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId(a.id().application().value()))); - zmsClient.deleteTenant(existingDomain); + applications.forEach(a -> zmsClient.addApplication(newDomain, new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId(a.id().application().value()), token)); + applications.forEach(a -> zmsClient.deleteApplication(existingDomain, new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId(a.id().application().value()), token)); + zmsClient.deleteTenant(existingDomain, token); log.info("Set Athenz domain for '" + tenant + "' from '" + existingDomain + "' to '" + newDomain + "'"); return tenant.with(newDomain); @@ -196,10 +198,10 @@ public class TenantController { } /** Delete an Athenz tenant */ - public void deleteTenant(AthenzTenant tenant, NToken nToken) { + public void deleteTenant(AthenzTenant tenant, OktaAccessToken token) { try (Lock lock = lock(tenant.name())) { deleteTenant(tenant.name()); - athenzClientFactory.createZmsClientWithAuthorizedServiceToken(nToken).deleteTenant(tenant.domain()); + zmsClient.deleteTenant(tenant.domain(), token); } } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/ApplicationAction.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/ApplicationAction.java new file mode 100644 index 00000000000..8614414dc95 --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/ApplicationAction.java @@ -0,0 +1,17 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.athenz; + +/** + * @author bjorncs + */ +public enum ApplicationAction { + deploy("deployer"), + read("reader"), + write("writer"); + + public final String roleName; + + ApplicationAction(String roleName) { + this.roleName = roleName; + } +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/HostedAthenzIdentities.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/HostedAthenzIdentities.java new file mode 100644 index 00000000000..08dbdff13db --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/HostedAthenzIdentities.java @@ -0,0 +1,27 @@ +// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.athenz; + +import com.yahoo.vespa.athenz.api.AthenzDomain; +import com.yahoo.vespa.athenz.api.AthenzService; +import com.yahoo.vespa.athenz.api.AthenzUser; +import com.yahoo.vespa.hosted.controller.api.identifiers.ScrewdriverId; +import com.yahoo.vespa.hosted.controller.api.identifiers.UserId; + +/** + * @author bjorncs + */ +public class HostedAthenzIdentities { + + public static final AthenzDomain SCREWDRIVER_DOMAIN = new AthenzDomain("cd.screwdriver.project"); + + private HostedAthenzIdentities() {} + + public static AthenzUser from(UserId userId) { + return AthenzUser.fromUserId(userId.id()); + } + + public static AthenzService from(ScrewdriverId screwdriverId) { + return new AthenzService(SCREWDRIVER_DOMAIN, "sd" + screwdriverId.id()); + } + +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzClientFactoryImpl.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzClientFactoryImpl.java index 633c0470080..846c90a96f5 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzClientFactoryImpl.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzClientFactoryImpl.java @@ -2,47 +2,33 @@ package com.yahoo.vespa.hosted.controller.athenz.impl; import com.google.inject.Inject; -import com.yahoo.athenz.auth.Principal; -import com.yahoo.athenz.auth.impl.PrincipalAuthority; -import com.yahoo.athenz.auth.impl.SimplePrincipal; -import com.yahoo.athenz.auth.token.PrincipalToken; -import com.yahoo.athenz.auth.util.Crypto; -import com.yahoo.athenz.zms.ZMSClient; -import com.yahoo.container.jdisc.secretstore.SecretStore; -import com.yahoo.vespa.athenz.api.AthenzIdentity; import com.yahoo.vespa.athenz.api.AthenzService; -import com.yahoo.vespa.athenz.api.NToken; +import com.yahoo.vespa.athenz.client.zms.DefaultZmsClient; +import com.yahoo.vespa.athenz.client.zms.ZmsClient; import com.yahoo.vespa.athenz.client.zts.DefaultZtsClient; import com.yahoo.vespa.athenz.client.zts.ZtsClient; import com.yahoo.vespa.athenz.identity.ServiceIdentityProvider; -import com.yahoo.vespa.athenz.utils.AthenzIdentities; import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactory; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsClient; import com.yahoo.vespa.hosted.controller.athenz.config.AthenzConfig; import java.net.URI; -import java.security.PrivateKey; /** * @author bjorncs */ public class AthenzClientFactoryImpl implements AthenzClientFactory { - private final SecretStore secretStore; private final AthenzConfig config; - private final AthenzPrincipalAuthority athenzPrincipalAuthority; private final ServiceIdentityProvider identityProvider; @Inject - public AthenzClientFactoryImpl(SecretStore secretStore, ServiceIdentityProvider identityProvider, AthenzConfig config) { - this.secretStore = secretStore; + public AthenzClientFactoryImpl(ServiceIdentityProvider identityProvider, AthenzConfig config) { this.identityProvider = identityProvider; this.config = config; - this.athenzPrincipalAuthority = new AthenzPrincipalAuthority(config.principalHeaderName()); } @Override - public AthenzIdentity getControllerIdentity() { + public AthenzService getControllerIdentity() { return identityProvider.identity(); } @@ -50,52 +36,16 @@ public class AthenzClientFactoryImpl implements AthenzClientFactory { * @return A ZMS client instance with the service identity as principal. */ @Override - public ZmsClient createZmsClientWithServicePrincipal() { - return new ZmsClientImpl(new ZMSClient(config.zmsUrl(), identityProvider.getIdentitySslContext()), config); + public ZmsClient createZmsClient() { + return new DefaultZmsClient(URI.create(config.zmsUrl()), identityProvider); } /** * @return A ZTS client instance with the service identity as principal. */ @Override - public ZtsClient createZtsClientWithServicePrincipal() { + public ZtsClient createZtsClient() { return new DefaultZtsClient(URI.create(config.ztsUrl()), identityProvider); } - /** - * @return A ZMS client created with a dual principal representing both the tenant admin and the service identity. - */ - @Override - public ZmsClient createZmsClientWithAuthorizedServiceToken(NToken authorizedServiceToken) { - PrincipalToken signedToken = new PrincipalToken(authorizedServiceToken.getRawToken()); - AthenzConfig.Service service = config.service(); - signedToken.signForAuthorizedService( - config.domain() + "." + service.name(), service.publicKeyId(), getServicePrivateKey()); - - Principal dualPrincipal = SimplePrincipal.create( - AthenzIdentities.USER_PRINCIPAL_DOMAIN.getName(), signedToken.getName(), signedToken.getSignedToken(), athenzPrincipalAuthority); - return new ZmsClientImpl(new ZMSClient(config.legacyZmsUrl(), dualPrincipal), config); - - } - - private PrivateKey getServicePrivateKey() { - AthenzConfig.Service service = config.service(); - String privateKey = secretStore.getSecret(service.privateKeySecretName(), service.privateKeyVersion()).trim(); - return Crypto.loadPrivateKey(privateKey); - } - - private static class AthenzPrincipalAuthority extends PrincipalAuthority { - private final String principalHeaderName; - - public AthenzPrincipalAuthority(String principalHeaderName) { - this.principalHeaderName = principalHeaderName; - } - - @Override - public String getHeader() { - return principalHeaderName; - } - } - - } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsClientFacade.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsClientFacade.java new file mode 100644 index 00000000000..09619a33cc4 --- /dev/null +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsClientFacade.java @@ -0,0 +1,122 @@ +// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.athenz.impl; + +import com.yahoo.log.LogLevel; +import com.yahoo.vespa.athenz.api.AthenzDomain; +import com.yahoo.vespa.athenz.api.AthenzIdentity; +import com.yahoo.vespa.athenz.api.AthenzResourceName; +import com.yahoo.vespa.athenz.api.AthenzRole; +import com.yahoo.vespa.athenz.api.AthenzService; +import com.yahoo.vespa.athenz.api.OktaAccessToken; +import com.yahoo.vespa.athenz.client.zms.RoleAction; +import com.yahoo.vespa.athenz.client.zms.ZmsClient; +import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId; +import com.yahoo.vespa.hosted.controller.athenz.ApplicationAction; + +import java.util.Arrays; +import java.util.List; +import java.util.Set; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +/** + * @author bjorncs + */ +public class ZmsClientFacade { + + private static final Logger log = Logger.getLogger(ZmsClientFacade.class.getName()); + private final ZmsClient zmsClient; + private final AthenzService service; + + public ZmsClientFacade(ZmsClient zmsClient, AthenzService identity) { + this.zmsClient = zmsClient; + this.service = identity; + } + + public void createTenant(AthenzDomain tenantDomain, OktaAccessToken token) { + log("createTenancy(tenantDomain=%s, service=%s)", tenantDomain, service); + zmsClient.createTenancy(tenantDomain, service, token); + } + + public void deleteTenant(AthenzDomain tenantDomain, OktaAccessToken token) { + log("deleteTenancy(tenantDomain=%s, service=%s)", tenantDomain, service); + zmsClient.deleteTenancy(tenantDomain, service, token); + } + + public void addApplication(AthenzDomain tenantDomain, ApplicationId applicationName, OktaAccessToken token) { + Set tenantRoleActions = createTenantRoleActions(); + log("createProviderResourceGroup(" + + "tenantDomain=%s, providerDomain=%s, service=%s, resourceGroup=%s, roleActions=%s)", + tenantDomain, service.getDomain().getName(), service.getName(), applicationName, tenantRoleActions); + zmsClient.createProviderResourceGroup(tenantDomain, service, applicationName.id(), tenantRoleActions, token); + } + + public void deleteApplication(AthenzDomain tenantDomain, ApplicationId applicationName, OktaAccessToken token) { + log("deleteProviderResourceGroup(tenantDomain=%s, providerDomain=%s, service=%s, resourceGroup=%s)", + tenantDomain, service.getDomain().getName(), service.getName(), applicationName); + zmsClient.deleteProviderResourceGroup(tenantDomain, service, applicationName.id(), token); + } + + public boolean hasApplicationAccess( + AthenzIdentity identity, ApplicationAction action, AthenzDomain tenantDomain, ApplicationId applicationName) { + return hasAccess( + action.name(), applicationResourceString(tenantDomain, applicationName), identity); + } + + public boolean hasTenantAdminAccess(AthenzIdentity identity, AthenzDomain tenantDomain) { + return hasAccess(TenantAction._modify_.name(), tenantResourceString(tenantDomain), identity); + } + + public boolean hasHostedOperatorAccess(AthenzIdentity identity) { + return hasAccess("modify", service.getDomain().getName() + ":hosted-vespa", identity); + } + + /** + * Used when creating tenancies. As there are no tenancy policies at this point, + * we cannot use {@link #hasTenantAdminAccess(AthenzIdentity, AthenzDomain)} + */ + public boolean isDomainAdmin(AthenzIdentity identity, AthenzDomain domain) { + log("getMembership(domain=%s, role=%s, principal=%s)", domain, "admin", identity); + return zmsClient.getMembership(new AthenzRole(domain, "admin"), identity); + } + + public List getDomainList(String prefix) { + log.log(LogLevel.DEBUG, String.format("getDomainList(prefix=%s)", prefix)); + return zmsClient.getDomainList(prefix); + } + + private static Set createTenantRoleActions() { + return Arrays.stream(ApplicationAction.values()) + .map(action -> new RoleAction(action.roleName, action.name())) + .collect(Collectors.toSet()); + } + + private boolean hasAccess(String action, String resource, AthenzIdentity identity) { + log("getAccess(action=%s, resource=%s, principal=%s)", action, resource, identity); + return zmsClient.hasAccess(AthenzResourceName.fromString(resource), action, identity); + } + + private static void log(String format, Object... args) { + log.log(LogLevel.DEBUG, String.format(format, args)); + } + + private String resourceStringPrefix(AthenzDomain tenantDomain) { + return String.format("%s:service.%s.tenant.%s", + service.getDomain().getName(), service.getName(), tenantDomain.getName()); + } + + private String tenantResourceString(AthenzDomain tenantDomain) { + return resourceStringPrefix(tenantDomain) + ".wildcard"; + } + + private String applicationResourceString(AthenzDomain tenantDomain, ApplicationId applicationName) { + return resourceStringPrefix(tenantDomain) + "." + "res_group" + "." + applicationName.id() + ".wildcard"; + } + + private enum TenantAction { + // This is meant to match only the '*' action of the 'admin' role. + // If needed, we can replace it with 'create', 'delete' etc. later. + _modify_ + } + +} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsClientImpl.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsClientImpl.java deleted file mode 100644 index 6179d9891fd..00000000000 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsClientImpl.java +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.athenz.impl; - -import com.yahoo.athenz.zms.DomainList; -import com.yahoo.athenz.zms.ProviderResourceGroupRoles; -import com.yahoo.athenz.zms.Tenancy; -import com.yahoo.athenz.zms.TenantRoleAction; -import com.yahoo.athenz.zms.ZMSClient; -import com.yahoo.athenz.zms.ZMSClientException; -import com.yahoo.log.LogLevel; -import com.yahoo.vespa.athenz.api.AthenzDomain; -import com.yahoo.vespa.athenz.api.AthenzIdentity; -import com.yahoo.vespa.athenz.api.AthenzService; -import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.ApplicationAction; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsClient; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsException; -import com.yahoo.vespa.hosted.controller.athenz.config.AthenzConfig; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.function.Supplier; -import java.util.logging.Logger; - -import static java.util.stream.Collectors.toList; - -/** - * @author bjorncs - */ -public class ZmsClientImpl implements ZmsClient { - - private static final Logger log = Logger.getLogger(ZmsClientImpl.class.getName()); - private final ZMSClient zmsClient; - private final AthenzService service; - - ZmsClientImpl(ZMSClient zmsClient, AthenzConfig config) { - this.zmsClient = zmsClient; - this.service = new AthenzService(config.domain(), config.service().name()); - } - - @Override - public void createTenant(AthenzDomain tenantDomain) { - log("putTenancy(tenantDomain=%s, service=%s)", tenantDomain, service); - runOrThrow(() -> { - Tenancy tenancy = new Tenancy() - .setDomain(tenantDomain.getName()) - .setService(service.getFullName()) - .setResourceGroups(Collections.emptyList()); - zmsClient.putTenancy(tenantDomain.getName(), service.getFullName(), /*auditref*/null, tenancy); - }); - } - - @Override - public void deleteTenant(AthenzDomain tenantDomain) { - log("deleteTenancy(tenantDomain=%s, service=%s)", tenantDomain, service); - runOrThrow(() -> zmsClient.deleteTenancy(tenantDomain.getName(), service.getFullName(), /*auditref*/null)); - } - - @Override - public void addApplication(AthenzDomain tenantDomain, ApplicationId applicationName) { - List tenantRoleActions = createTenantRoleActions(); - log("putProviderResourceGroupRoles(" + - "tenantDomain=%s, providerDomain=%s, service=%s, resourceGroup=%s, roleActions=%s)", - tenantDomain, service.getDomain().getName(), service.getName(), applicationName, tenantRoleActions); - runOrThrow(() -> { - ProviderResourceGroupRoles resourceGroupRoles = new ProviderResourceGroupRoles() - .setDomain(service.getDomain().getName()) - .setService(service.getName()) - .setTenant(tenantDomain.getName()) - .setResourceGroup(applicationName.id()) - .setRoles(tenantRoleActions); - zmsClient.putProviderResourceGroupRoles( - tenantDomain.getName(), service.getDomain().getName(), service.getName(), - applicationName.id(), /*auditref*/null, resourceGroupRoles); - }); - } - - @Override - public void deleteApplication(AthenzDomain tenantDomain, ApplicationId applicationName) { - log("deleteProviderResourceGroupRoles(tenantDomain=%s, providerDomain=%s, service=%s, resourceGroup=%s)", - tenantDomain, service.getDomain().getName(), service.getName(), applicationName); - runOrThrow(() -> { - zmsClient.deleteProviderResourceGroupRoles( - tenantDomain.getName(), service.getDomain().getName(), service.getName(), applicationName.id(), /*auditref*/null); - }); - } - - @Override - public boolean hasApplicationAccess( - AthenzIdentity identity, ApplicationAction action, AthenzDomain tenantDomain, ApplicationId applicationName) { - return hasAccess( - action.name(), applicationResourceString(tenantDomain, applicationName), identity); - } - - @Override - public boolean hasTenantAdminAccess(AthenzIdentity identity, AthenzDomain tenantDomain) { - return hasAccess(TenantAction._modify_.name(), tenantResourceString(tenantDomain), identity); - } - - @Override - public boolean hasHostedOperatorAccess(AthenzIdentity identity) { - return getOrThrow(() -> hasAccess("modify", service.getDomain().getName() + ":hosted-vespa", identity)); - } - - /** - * Used when creating tenancies. As there are no tenancy policies at this point, - * we cannot use {@link #hasTenantAdminAccess(AthenzIdentity, AthenzDomain)} - */ - @Override - public boolean isDomainAdmin(AthenzIdentity identity, AthenzDomain domain) { - log("getMembership(domain=%s, role=%s, principal=%s)", domain, "admin", identity); - return getOrThrow( - () -> zmsClient.getMembership(domain.getName(), "admin", identity.getFullName()).getIsMember()); - } - - @Override - public List getDomainList(String prefix) { - log.log(LogLevel.DEBUG, String.format("getDomainList(prefix=%s)", prefix)); - return getOrThrow( - () -> { - DomainList domainList = zmsClient.getDomainList( - /*limit*/null, /*skip*/null, prefix, /*depth*/null, /*domain*/null, - /*productId*/ null, /*modifiedSince*/null); - return toAthenzDomains(domainList.getNames()); - }); - } - - private static List createTenantRoleActions() { - return Arrays.stream(ApplicationAction.values()) - .map(action -> new TenantRoleAction().setAction(action.name()).setRole(action.roleName)) - .collect(toList()); - } - - private static List toAthenzDomains(List domains) { - return domains.stream().map(AthenzDomain::new).collect(toList()); - } - - private boolean hasAccess(String action, String resource, AthenzIdentity identity) { - log("getAccess(action=%s, resource=%s, principal=%s)", action, resource, identity); - return getOrThrow( - () -> zmsClient.getAccess(action, resource, /*trustDomain*/null, identity.getFullName()) - .getGranted()); - } - - private static void log(String format, Object... args) { - log.log(LogLevel.DEBUG, String.format(format, args)); - } - - private static void runOrThrow(Runnable wrappedCode) { - try { - wrappedCode.run(); - } catch (ZMSClientException e) { - logWarning(e); - throw new ZmsException(e.getCode(), e); - } - } - - private static T getOrThrow(Supplier wrappedCode) { - try { - return wrappedCode.get(); - } catch (ZMSClientException e) { - logWarning(e); - throw new ZmsException(e.getCode(), e); - } - } - - private static void logWarning(ZMSClientException e) { - log.warning("Error from Athenz: " + e.getMessage()); - } - - private String resourceStringPrefix(AthenzDomain tenantDomain) { - return String.format("%s:service.%s.tenant.%s", - service.getDomain().getName(), service.getName(), tenantDomain.getName()); - } - - private String tenantResourceString(AthenzDomain tenantDomain) { - return resourceStringPrefix(tenantDomain) + ".wildcard"; - } - - private String applicationResourceString(AthenzDomain tenantDomain, ApplicationId applicationName) { - return resourceStringPrefix(tenantDomain) + "." + "res_group" + "." + applicationName.id() + ".wildcard"; - } - - private enum TenantAction { - // This is meant to match only the '*' action of the 'admin' role. - // If needed, we can replace it with 'create', 'delete' etc. later. - _modify_ - } - -} diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzClientFactoryMock.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzClientFactoryMock.java index 6f829113016..f9f449121e0 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzClientFactoryMock.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzClientFactoryMock.java @@ -3,11 +3,9 @@ package com.yahoo.vespa.hosted.controller.athenz.mock; import com.google.inject.Inject; import com.yahoo.component.AbstractComponent; -import com.yahoo.vespa.athenz.api.AthenzIdentity; import com.yahoo.vespa.athenz.api.AthenzService; -import com.yahoo.vespa.athenz.api.NToken; +import com.yahoo.vespa.athenz.client.zms.ZmsClient; import com.yahoo.vespa.athenz.client.zts.ZtsClient; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsClient; import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactory; import java.util.logging.Level; @@ -36,28 +34,20 @@ public class AthenzClientFactoryMock extends AbstractComponent implements Athenz } @Override - public AthenzIdentity getControllerIdentity() { + public AthenzService getControllerIdentity() { return new AthenzService("vespa.hosting"); } @Override - public ZmsClient createZmsClientWithServicePrincipal() { - log("createZmsClientWithServicePrincipal()"); - return new ZmsClientMock(athenz); + public ZmsClient createZmsClient() { + return new ZmsClientMock(athenz, getControllerIdentity()); } @Override - public ZtsClient createZtsClientWithServicePrincipal() { - log("createZtsClientWithServicePrincipal()"); + public ZtsClient createZtsClient() { return new ZtsClientMock(athenz); } - @Override - public ZmsClient createZmsClientWithAuthorizedServiceToken(NToken authorizedServiceToken) { - log("createZmsClientWithAuthorizedServiceToken(authorizedServiceToken='%s')", authorizedServiceToken); - return new ZmsClientMock(athenz); - } - private static void log(String format, Object... args) { log.log(Level.INFO, String.format(format, args)); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzDbMock.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzDbMock.java index 0a360184da9..a11426b9a23 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzDbMock.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzDbMock.java @@ -4,7 +4,7 @@ package com.yahoo.vespa.hosted.controller.athenz.mock; import com.yahoo.vespa.athenz.api.AthenzDomain; import com.yahoo.vespa.athenz.api.AthenzIdentity; import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.ApplicationAction; +import com.yahoo.vespa.hosted.controller.athenz.ApplicationAction; import java.util.ArrayList; import java.util.HashMap; diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/ZmsClientMock.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/ZmsClientMock.java index 5e8674ce637..f7a8e702b06 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/ZmsClientMock.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/ZmsClientMock.java @@ -1,18 +1,26 @@ // Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.athenz.mock; -import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId; import com.yahoo.vespa.athenz.api.AthenzDomain; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.ApplicationAction; import com.yahoo.vespa.athenz.api.AthenzIdentity; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsClient; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsException; +import com.yahoo.vespa.athenz.api.AthenzResourceName; +import com.yahoo.vespa.athenz.api.AthenzRole; +import com.yahoo.vespa.athenz.api.AthenzService; +import com.yahoo.vespa.athenz.api.OktaAccessToken; +import com.yahoo.vespa.athenz.client.zms.RoleAction; +import com.yahoo.vespa.athenz.client.zms.ZmsClient; +import com.yahoo.vespa.athenz.client.zms.ZmsClientException; +import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId; +import com.yahoo.vespa.hosted.controller.athenz.ApplicationAction; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * @author bjorncs @@ -22,20 +30,24 @@ public class ZmsClientMock implements ZmsClient { private static final Logger log = Logger.getLogger(ZmsClientMock.class.getName()); private final AthenzDbMock athenz; + private final AthenzService controllerIdentity; + private static final Pattern TENANT_RESOURCE_PATTERN = Pattern.compile("service\\.hosting\\.tenant\\.(?[\\w\\-_]+)\\..*"); + private static final Pattern APPLICATION_RESOURCE_PATTERN = Pattern.compile("service\\.hosting\\.tenant\\.[\\w\\-_]+\\.res_group\\.(?[\\w\\-_]+)\\.wildcard"); - public ZmsClientMock(AthenzDbMock athenz) { + public ZmsClientMock(AthenzDbMock athenz, AthenzService controllerIdentity) { this.athenz = athenz; + this.controllerIdentity = controllerIdentity; } @Override - public void createTenant(AthenzDomain tenantDomain) { - log("createTenant(tenantDomain='%s')", tenantDomain); + public void createTenancy(AthenzDomain tenantDomain, AthenzService providerService, OktaAccessToken token) { + log("createTenancy(tenantDomain='%s')", tenantDomain); getDomainOrThrow(tenantDomain, false).isVespaTenant = true; } @Override - public void deleteTenant(AthenzDomain tenantDomain) { - log("deleteTenant(tenantDomain='%s')", tenantDomain); + public void deleteTenancy(AthenzDomain tenantDomain, AthenzService providerService, OktaAccessToken token) { + log("deleteTenancy(tenantDomain='%s')", tenantDomain); AthenzDbMock.Domain domain = getDomainOrThrow(tenantDomain, false); domain.isVespaTenant = false; domain.applications.clear(); @@ -43,55 +55,80 @@ public class ZmsClientMock implements ZmsClient { } @Override - public void addApplication(AthenzDomain tenantDomain, ApplicationId applicationName) { - log("addApplication(tenantDomain='%s', applicationName='%s')", tenantDomain, applicationName); + public void createProviderResourceGroup(AthenzDomain tenantDomain, AthenzService providerService, String resourceGroup, Set roleActions, OktaAccessToken token) { + log("createProviderResourceGroup(tenantDomain='%s', resourceGroup='%s')", tenantDomain, resourceGroup); AthenzDbMock.Domain domain = getDomainOrThrow(tenantDomain, true); - if (!domain.applications.containsKey(applicationName)) { - domain.applications.put(applicationName, new AthenzDbMock.Application()); + ApplicationId applicationId = new ApplicationId(resourceGroup); + if (!domain.applications.containsKey(applicationId)) { + domain.applications.put(applicationId, new AthenzDbMock.Application()); } } @Override - public void deleteApplication(AthenzDomain tenantDomain, ApplicationId applicationName) { - log("addApplication(tenantDomain='%s', applicationName='%s')", tenantDomain, applicationName); - getDomainOrThrow(tenantDomain, true).applications.remove(applicationName); + public void deleteProviderResourceGroup(AthenzDomain tenantDomain, AthenzService providerService, String resourceGroup, OktaAccessToken token) { + log("deleteProviderResourceGroup(tenantDomain='%s', resourceGroup='%s')", tenantDomain, resourceGroup); + getDomainOrThrow(tenantDomain, true).applications.remove(new ApplicationId(resourceGroup)); } @Override - public boolean hasApplicationAccess(AthenzIdentity identity, ApplicationAction action, AthenzDomain tenantDomain, ApplicationId applicationName) { - log("hasApplicationAccess(principal='%s', action='%s', tenantDomain='%s', applicationName='%s')", - identity, action, tenantDomain, applicationName); - AthenzDbMock.Domain domain = getDomainOrThrow(tenantDomain, true); - AthenzDbMock.Application application = domain.applications.get(applicationName); - if (application == null) { - throw zmsException(400, "Application '%s' not found", applicationName); + public boolean getMembership(AthenzRole role, AthenzIdentity identity) { + if (role.roleName().equals("admin")) { + return getDomainOrThrow(role.domain(), false).admins.contains(identity); } - return isHostedOperator(identity) || domain.admins.contains(identity) || application.acl.get(action).contains(identity); + return false; } @Override - public boolean hasTenantAdminAccess(AthenzIdentity identity, AthenzDomain tenantDomain) { - log("hasTenantAdminAccess(principal='%s', tenantDomain='%s')", identity, tenantDomain); - return isHostedOperator(identity) || isDomainAdmin(identity, tenantDomain) || - getDomainOrThrow(tenantDomain, true).tenantAdmins.contains(identity); + public List getDomainList(String prefix) { + log("getDomainList()"); + return new ArrayList<>(athenz.domains.keySet()); } @Override - public boolean hasHostedOperatorAccess(AthenzIdentity identity) { - log("hasHostedOperatorAccess(identity='%s')", identity); - return isHostedOperator(identity); + public boolean hasAccess(AthenzResourceName resource, String action, AthenzIdentity identity) { + log("hasAccess(resource=%s, action=%s, identity=%s)", resource, action, identity); + if (resource.getDomain().equals(this.controllerIdentity.getDomain())) { + if (isHostedOperator(identity)) { + return true; + } + if (resource.getEntityName().startsWith("service.hosting.tenant.")) { + AthenzDomain tenantDomainName = getTenantDomain(resource); + AthenzDbMock.Domain tenantDomain = getDomainOrThrow(tenantDomainName, true); + if (tenantDomain.admins.contains(identity)) { + return true; + } + if (resource.getEntityName().contains(".res_group.")) { + ApplicationId applicationName = new ApplicationId(getResourceGroupName(resource)); + AthenzDbMock.Application application = tenantDomain.applications.get(applicationName); + if (application == null) { + throw zmsException(400, "Application '%s' not found", applicationName); + } + return application.acl.get(ApplicationAction.valueOf(action)).contains(identity); + } + return false; + } + return false; + } + return false; } @Override - public boolean isDomainAdmin(AthenzIdentity identity, AthenzDomain domain) { - log("isDomainAdmin(principal='%s', domain='%s')", identity, domain); - return getDomainOrThrow(domain, false).admins.contains(identity); + public void close() {} + + private static AthenzDomain getTenantDomain(AthenzResourceName resource) { + Matcher matcher = TENANT_RESOURCE_PATTERN.matcher(resource.getEntityName()); + if (!matcher.matches()) { + throw new IllegalArgumentException(resource.toResourceNameString()); + } + return new AthenzDomain(matcher.group("tenantDomain")); } - @Override - public List getDomainList(String prefix) { - log("getDomainList()"); - return new ArrayList<>(athenz.domains.keySet()); + private static String getResourceGroupName(AthenzResourceName resource) { + Matcher matcher = APPLICATION_RESOURCE_PATTERN.matcher(resource.getEntityName()); + if (!matcher.matches()) { + throw new IllegalArgumentException(resource.toResourceNameString()); + } + return matcher.group("resourceGroup"); } private AthenzDbMock.Domain getDomainOrThrow(AthenzDomain domainName, boolean verifyVespaTenant) { @@ -107,8 +144,8 @@ public class ZmsClientMock implements ZmsClient { return athenz.hostedOperators.contains(identity); } - private static ZmsException zmsException(int code, String message, Object... args) { - return new ZmsException(code, String.format(message, args)); + private static ZmsClientException zmsException(int code, String message, Object... args) { + return new ZmsClientException(code, String.format(message, args)); } private static void log(String format, Object... args) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java index 57133986654..554f9ccd9b5 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java @@ -24,7 +24,8 @@ 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.api.NToken; +import com.yahoo.vespa.athenz.api.OktaAccessToken; +import com.yahoo.vespa.athenz.client.zms.ZmsClientException; import com.yahoo.vespa.config.SlimeUtils; import com.yahoo.vespa.hosted.controller.AlreadyExistsException; import com.yahoo.vespa.hosted.controller.Application; @@ -46,7 +47,6 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId; 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.AthenzClientFactory; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.ZmsException; import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException; import com.yahoo.vespa.hosted.controller.api.integration.configserver.Log; import com.yahoo.vespa.hosted.controller.api.integration.configserver.Logs; @@ -65,6 +65,7 @@ import com.yahoo.vespa.hosted.controller.application.DeploymentMetrics; import com.yahoo.vespa.hosted.controller.application.JobStatus; import com.yahoo.vespa.hosted.controller.application.RotationStatus; import com.yahoo.vespa.hosted.controller.application.SourceRevision; +import com.yahoo.vespa.hosted.controller.athenz.impl.ZmsClientFacade; import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse; import com.yahoo.vespa.hosted.controller.restapi.MessageResponse; import com.yahoo.vespa.hosted.controller.restapi.ResourceResponse; @@ -109,7 +110,7 @@ import java.util.stream.Collectors; public class ApplicationApiHandler extends LoggingRequestHandler { private final Controller controller; - private final AthenzClientFactory athenzClientFactory; + private final ZmsClientFacade zmsClient; @Inject public ApplicationApiHandler(LoggingRequestHandler.Context parentCtx, @@ -117,7 +118,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { AthenzClientFactory athenzClientFactory) { super(parentCtx); this.controller = controller; - this.athenzClientFactory = athenzClientFactory; + this.zmsClient = new ZmsClientFacade(athenzClientFactory.createZmsClient(), athenzClientFactory.getControllerIdentity()); } @Override @@ -675,7 +676,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { if ( ! tenant.isPresent()) return ErrorResponse.notFoundError("Tenant '" + tenantName + "' does not exist"); Inspector requestData = toSlime(request.getData()).get(); - NToken token = requireNToken(request, "Could not update " + tenantName); + OktaAccessToken token = requireOktaAccessToken(request, "Could not update " + tenantName); controller.tenants().lockOrThrow(tenant.get().name(), lockedTenant -> { lockedTenant = lockedTenant.with(new Property(mandatory("property", requestData).asString())); @@ -702,17 +703,17 @@ public class ApplicationApiHandler extends LoggingRequestHandler { new Property(mandatory("property", requestData).asString()), optional("propertyId", requestData).map(PropertyId::new)); throwIfNotAthenzDomainAdmin(tenant.domain(), request); - controller.tenants().create(tenant, requireNToken(request, "Could not create " + tenantName)); + controller.tenants().create(tenant, requireOktaAccessToken(request, "Could not create " + tenantName)); return tenant(tenant, request, true); } private HttpResponse createApplication(String tenantName, String applicationName, HttpRequest request) { Application application; try { - application = controller.applications().createApplication(ApplicationId.from(tenantName, applicationName, "default"), getUserPrincipal(request).getNToken()); + application = controller.applications().createApplication(ApplicationId.from(tenantName, applicationName, "default"), getOktaAccessToken(request)); } - catch (ZmsException e) { // TODO: Push conversion down - if (e.getCode() == com.yahoo.jdisc.Response.Status.FORBIDDEN) + catch (ZmsClientException e) { // TODO: Push conversion down + if (e.getErrorCode() == com.yahoo.jdisc.Response.Status.FORBIDDEN) throw new ForbiddenException("Not authorized to create application", e); else throw e; @@ -828,7 +829,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { if (tenant.get() instanceof AthenzTenant) { controller.tenants().deleteTenant((AthenzTenant) tenant.get(), - requireNToken(request, "Could not delete " + tenantName)); + requireOktaAccessToken(request, "Could not delete " + tenantName)); } else if (tenant.get() instanceof UserTenant) { controller.tenants().deleteTenant((UserTenant) tenant.get()); } else { @@ -842,7 +843,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { private HttpResponse deleteApplication(String tenantName, String applicationName, HttpRequest request) { ApplicationId id = ApplicationId.from(tenantName, applicationName, "default"); - controller.applications().deleteApplication(id, getUserPrincipal(request).getNToken()); + controller.applications().deleteApplication(id, getOktaAccessToken(request)); return new EmptyJsonResponse(); // TODO: Replicates current behavior but should return a message response instead } @@ -1019,8 +1020,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { private void throwIfNotAthenzDomainAdmin(AthenzDomain tenantDomain, HttpRequest request) { AthenzIdentity identity = getUserPrincipal(request).getIdentity(); - boolean isDomainAdmin = athenzClientFactory.createZmsClientWithServicePrincipal() - .isDomainAdmin(identity, tenantDomain); + boolean isDomainAdmin = zmsClient.isDomainAdmin(identity, tenantDomain); if ( ! isDomainAdmin) { throw new ForbiddenException( String.format("The user '%s' is not admin in Athenz domain '%s'", identity.getFullName(), tenantDomain.getName())); @@ -1212,9 +1212,14 @@ public class ApplicationApiHandler extends LoggingRequestHandler { throw new IllegalArgumentException("Unknown tenant type: " + tenant.getClass().getSimpleName()); } - private static NToken requireNToken(HttpRequest request, String message) { - return getUserPrincipal(request).getNToken().orElseThrow(() -> new IllegalArgumentException( - message + ": No NToken provided")); + private static OktaAccessToken requireOktaAccessToken(HttpRequest request, String message) { + return getOktaAccessToken(request) + .orElseThrow(() -> new IllegalArgumentException(message + ": No Okta Access Token provided")); + } + + private static Optional getOktaAccessToken(HttpRequest request) { + return Optional.ofNullable(request.getHeader(OktaAccessToken.HTTP_HEADER_NAME)) + .map(OktaAccessToken::new); } private static ApplicationId appIdFromPath(Path path) { 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 33d53b0becf..3777430d0a9 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 @@ -9,16 +9,17 @@ 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.ApplicationAction; +import com.yahoo.vespa.hosted.controller.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.restapi.Path; +import com.yahoo.vespa.hosted.controller.athenz.impl.ZmsClientFacade; import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant; import com.yahoo.vespa.hosted.controller.tenant.Tenant; import com.yahoo.vespa.hosted.controller.tenant.UserTenant; @@ -40,7 +41,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.athenz.HostedAthenzIdentities.SCREWDRIVER_DOMAIN; /** * A security filter protects all controller apis. @@ -55,7 +56,7 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase { private static final Logger log = Logger.getLogger(ControllerAuthorizationFilter.class.getName()); - private final AthenzClientFactory clientFactory; + private final ZmsClientFacade zmsClient; private final TenantController tenantController; @Inject @@ -63,7 +64,7 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase { Controller controller, CorsFilterConfig corsConfig) { super(corsConfig); - this.clientFactory = clientFactory; + this.zmsClient = new ZmsClientFacade(clientFactory.createZmsClient(), clientFactory.getControllerIdentity()); this.tenantController = controller.tenants(); } @@ -71,7 +72,7 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase { TenantController tenantController, Set allowedUrls) { super(allowedUrls); - this.clientFactory = clientFactory; + this.zmsClient = new ZmsClientFacade(clientFactory.createZmsClient(), clientFactory.getControllerIdentity());; this.tenantController = tenantController; } @@ -151,8 +152,7 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase { } private boolean isHostedOperator(AthenzIdentity identity) { - return clientFactory.createZmsClientWithServicePrincipal() - .hasHostedOperatorAccess(identity); + return zmsClient.hasHostedOperatorAccess(identity); } private void verifyIsTenantAdmin(AthenzPrincipal principal, TenantName name) { @@ -166,8 +166,7 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase { private boolean isTenantAdmin(AthenzIdentity identity, Tenant tenant) { if (tenant instanceof AthenzTenant) { - return clientFactory.createZmsClientWithServicePrincipal() - .hasTenantAdminAccess(identity, ((AthenzTenant) tenant).domain()); + return zmsClient.hasTenantAdminAccess(identity, ((AthenzTenant) tenant).domain()); } else if (tenant instanceof UserTenant) { if (!(identity instanceof AthenzUser)) { return false; @@ -210,13 +209,13 @@ public class ControllerAuthorizationFilter extends CorsRequestFilterBase { private boolean hasDeployerAccess(AthenzIdentity identity, AthenzDomain tenantDomain, ApplicationName application) { try { - return clientFactory.createZmsClientWithServicePrincipal() + return zmsClient .hasApplicationAccess( identity, ApplicationAction.deploy, tenantDomain, new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId(application.value())); - } catch (ZmsException e) { + } catch (ZmsClientException e) { throw new InternalServerErrorException("Failed to authorize operation: (" + e.getMessage() + ")", e); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java index b96e2112a5c..e4cfbbc6a26 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java @@ -11,7 +11,7 @@ import com.yahoo.config.provision.InstanceName; import com.yahoo.config.provision.RegionName; import com.yahoo.config.provision.SystemName; import com.yahoo.config.provision.TenantName; -import com.yahoo.vespa.athenz.api.NToken; +import com.yahoo.vespa.athenz.api.OktaAccessToken; import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions; import com.yahoo.vespa.hosted.controller.api.application.v4.model.EndpointStatus; import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; @@ -320,7 +320,7 @@ public class ControllerTest { tester.deployAndNotify(app1, applicationPackage, true, systemTest); tester.applications().deactivate(app1.id(), ZoneId.from(Environment.test, RegionName.from("us-east-1"))); tester.applications().deactivate(app1.id(), ZoneId.from(Environment.staging, RegionName.from("us-east-3"))); - tester.applications().deleteApplication(app1.id(), Optional.of(new NToken("ntoken"))); + tester.applications().deleteApplication(app1.id(), Optional.of(new OktaAccessToken("okta-token"))); try (RotationLock lock = tester.applications().rotationRepository().lock()) { assertTrue("Rotation is unassigned", tester.applications().rotationRepository().availableRotations(lock) diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java index 287fbe8c36d..8994c68acf3 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java @@ -8,6 +8,7 @@ import com.yahoo.config.provision.TenantName; import com.yahoo.slime.Slime; import com.yahoo.test.ManualClock; import com.yahoo.vespa.athenz.api.AthenzDomain; +import com.yahoo.vespa.athenz.api.OktaAccessToken; import com.yahoo.vespa.curator.Lock; import com.yahoo.vespa.curator.mock.MockCurator; import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions; @@ -247,14 +248,14 @@ public final class ControllerTester { Optional.ofNullable(propertyId) .map(Object::toString) .map(PropertyId::new)); - controller().tenants().create(tenant, TestIdentities.userNToken); + controller().tenants().create(tenant, new OktaAccessToken("okta-token")); assertNotNull(controller().tenants().tenant(name)); return name; } public Application createApplication(TenantName tenant, String applicationName, String instanceName, long projectId) { ApplicationId applicationId = ApplicationId.from(tenant.value(), applicationName, instanceName); - controller().applications().createApplication(applicationId, Optional.of(TestIdentities.userNToken)); + controller().applications().createApplication(applicationId, Optional.of(new OktaAccessToken("okta-token"))); controller().applications().lockOrThrow(applicationId, lockedApplication -> controller().applications().store(lockedApplication.withProjectId(OptionalLong.of(projectId)))); return controller().applications().require(applicationId); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/TestIdentities.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/TestIdentities.java deleted file mode 100644 index 18d3e92620d..00000000000 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/TestIdentities.java +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller; - -import com.yahoo.vespa.athenz.api.NToken; -import com.yahoo.vespa.hosted.controller.api.identifiers.EnvironmentId; -import com.yahoo.vespa.hosted.controller.api.identifiers.InstanceId; -import com.yahoo.vespa.hosted.controller.api.identifiers.Property; -import com.yahoo.vespa.hosted.controller.api.identifiers.RegionId; - -/** - * @author Tony Vaagenes - */ -public class TestIdentities { - - public static final EnvironmentId environment = new EnvironmentId("dev"); - - public static final RegionId region = new RegionId("us-east-1"); - - public static final InstanceId instance = new InstanceId("default"); - - public static final Property property = new Property("property"); - - public static final NToken userNToken = new NToken("dummy"); - -} diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/AthenzFilterMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/AthenzFilterMock.java index 4acd3a34c8d..72027234b28 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/AthenzFilterMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/AthenzFilterMock.java @@ -13,7 +13,6 @@ import com.yahoo.jdisc.http.filter.DiscFilterRequest; import com.yahoo.jdisc.http.filter.SecurityRequestFilter; import com.yahoo.vespa.athenz.api.AthenzIdentity; import com.yahoo.vespa.athenz.api.AthenzPrincipal; -import com.yahoo.vespa.athenz.api.NToken; import com.yahoo.vespa.athenz.utils.AthenzIdentities; import com.yahoo.yolean.chain.Before; @@ -24,7 +23,6 @@ import com.yahoo.yolean.chain.Before; public class AthenzFilterMock implements SecurityRequestFilter { public static final String IDENTITY_HEADER_NAME = "Athenz-Identity"; - public static final String ATHENZ_NTOKEN_HEADER_NAME = "Athenz-NToken"; private static final ObjectMapper mapper = new ObjectMapper(); @@ -32,7 +30,6 @@ public class AthenzFilterMock implements SecurityRequestFilter { public void filter(DiscFilterRequest request, ResponseHandler handler) { if (request.getMethod().equalsIgnoreCase("OPTIONS")) return; String identityName = request.getHeader(IDENTITY_HEADER_NAME); - String nToken = request.getHeader(ATHENZ_NTOKEN_HEADER_NAME); if (identityName == null) { Response response = new Response(HttpResponse.Status.UNAUTHORIZED); response.headers().put("Content-Type", "application/json"); @@ -45,10 +42,7 @@ public class AthenzFilterMock implements SecurityRequestFilter { } } else { AthenzIdentity identity = AthenzIdentities.from(identityName); - AthenzPrincipal principal = - nToken == null ? - new AthenzPrincipal(identity) : - new AthenzPrincipal(identity, new NToken(nToken)); + AthenzPrincipal principal = new AthenzPrincipal(identity); request.setUserPrincipal(principal); } } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainerTest.java index b950e969300..92f3e87f001 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainerTest.java @@ -4,7 +4,7 @@ package com.yahoo.vespa.hosted.controller.maintenance; import com.yahoo.config.application.api.ValidationId; import com.yahoo.config.provision.Environment; import com.yahoo.config.provision.RegionName; -import com.yahoo.vespa.athenz.api.NToken; +import com.yahoo.vespa.athenz.api.OktaAccessToken; import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.api.integration.dns.Record; import com.yahoo.vespa.hosted.controller.api.integration.dns.RecordName; @@ -88,7 +88,7 @@ public class DnsMaintainerTest { tester.deployAndNotify(application, applicationPackage, true, systemTest); tester.applications().deactivate(application.id(), ZoneId.from(Environment.test, RegionName.from("us-east-1"))); tester.applications().deactivate(application.id(), ZoneId.from(Environment.staging, RegionName.from("us-east-3"))); - tester.applications().deleteApplication(application.id(), Optional.of(new NToken("ntoken"))); + tester.applications().deleteApplication(application.id(), Optional.of(new OktaAccessToken("okta-token"))); // DnsMaintainer removes records dnsMaintainer.maintain(); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java index e506c8c56ca..42178e460bd 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java @@ -3,7 +3,7 @@ package com.yahoo.vespa.hosted.controller.maintenance; import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; -import com.yahoo.vespa.hosted.controller.TestIdentities; +import com.yahoo.vespa.athenz.api.OktaAccessToken; import com.yahoo.vespa.hosted.controller.api.integration.deployment.RunId; import com.yahoo.vespa.hosted.controller.application.ApplicationVersion; import com.yahoo.vespa.hosted.controller.application.SourceRevision; @@ -49,11 +49,11 @@ import static com.yahoo.vespa.hosted.controller.deployment.Step.deactivateReal; import static com.yahoo.vespa.hosted.controller.deployment.Step.deactivateTester; import static com.yahoo.vespa.hosted.controller.deployment.Step.deployReal; import static com.yahoo.vespa.hosted.controller.deployment.Step.deployTester; +import static com.yahoo.vespa.hosted.controller.deployment.Step.endTests; import static com.yahoo.vespa.hosted.controller.deployment.Step.installReal; import static com.yahoo.vespa.hosted.controller.deployment.Step.installTester; import static com.yahoo.vespa.hosted.controller.deployment.Step.report; import static com.yahoo.vespa.hosted.controller.deployment.Step.startTests; -import static com.yahoo.vespa.hosted.controller.deployment.Step.endTests; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -211,7 +211,7 @@ public class JobRunnerTest { // Thread is still trying to deploy tester -- delete application, and see all data is garbage collected. assertEquals(Collections.singletonList(runId), jobs.active().stream().map(run -> run.id()).collect(Collectors.toList())); - tester.controller().applications().deleteApplication(id, Optional.of(TestIdentities.userNToken)); + tester.controller().applications().deleteApplication(id, Optional.of(new OktaAccessToken("okta-token"))); assertEquals(Collections.emptyList(), jobs.active()); assertEquals(runId, jobs.last(id, systemTest).get().id()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java index b571e6f1c48..11e8a82dd42 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java @@ -6,16 +6,16 @@ import com.yahoo.application.container.handler.Request; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.TenantName; import com.yahoo.vespa.athenz.api.AthenzDomain; +import com.yahoo.vespa.athenz.api.OktaAccessToken; import com.yahoo.vespa.athenz.utils.AthenzIdentities; import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.Controller; -import com.yahoo.vespa.hosted.controller.TestIdentities; import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions; import com.yahoo.vespa.hosted.controller.api.identifiers.Property; import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId; import com.yahoo.vespa.hosted.controller.api.identifiers.ScrewdriverId; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.ApplicationAction; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.HostedAthenzIdentities; +import com.yahoo.vespa.hosted.controller.athenz.ApplicationAction; +import com.yahoo.vespa.hosted.controller.athenz.HostedAthenzIdentities; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockBuildService; import com.yahoo.vespa.hosted.controller.api.integration.zone.ZoneId; @@ -77,9 +77,9 @@ public class ContainerControllerTester { controller().tenants().create(AthenzTenant.create(TenantName.from(tenant), domain1, new Property("property1"), Optional.of(new PropertyId("1234"))), - TestIdentities.userNToken); + new OktaAccessToken("okta-token")); ApplicationId app = ApplicationId.from(tenant, application, "default"); - return controller().applications().createApplication(app, Optional.of(TestIdentities.userNToken)); + return controller().applications().createApplication(app, Optional.of(new OktaAccessToken("okta-token"))); } public Application deploy(Application application, ApplicationPackage applicationPackage, ZoneId zone) { 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 e47cdf887f9..8364261696c 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 @@ -7,7 +7,7 @@ import com.yahoo.application.container.handler.Request; import com.yahoo.application.container.handler.Response; import com.yahoo.vespa.athenz.api.AthenzIdentity; import com.yahoo.vespa.athenz.api.AthenzUser; -import com.yahoo.vespa.athenz.api.NToken; +import com.yahoo.vespa.athenz.api.OktaAccessToken; import com.yahoo.vespa.hosted.controller.athenz.mock.AthenzClientFactoryMock; import org.junit.After; import org.junit.Before; @@ -15,7 +15,6 @@ import org.junit.Before; import java.io.UncheckedIOException; import java.nio.charset.CharacterCodingException; -import static com.yahoo.vespa.hosted.controller.integration.AthenzFilterMock.ATHENZ_NTOKEN_HEADER_NAME; import static com.yahoo.vespa.hosted.controller.integration.AthenzFilterMock.IDENTITY_HEADER_NAME; import static org.junit.Assert.assertEquals; @@ -152,8 +151,8 @@ public class ControllerContainerTest { return request; } - protected static Request addNTokenToRequest(Request request, NToken nToken) { - request.getHeaders().put(ATHENZ_NTOKEN_HEADER_NAME, nToken.getRawToken()); + protected static Request addOktaAccessToken(Request request, OktaAccessToken token) { + request.getHeaders().put(OktaAccessToken.HTTP_HEADER_NAME, token.token()); return request; } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java index d620d78d3f0..27339cf4859 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java @@ -16,7 +16,7 @@ import com.yahoo.slime.Slime; import com.yahoo.vespa.athenz.api.AthenzDomain; import com.yahoo.vespa.athenz.api.AthenzIdentity; import com.yahoo.vespa.athenz.api.AthenzUser; -import com.yahoo.vespa.athenz.api.NToken; +import com.yahoo.vespa.athenz.api.OktaAccessToken; import com.yahoo.vespa.config.SlimeUtils; import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.api.application.v4.EnvironmentResource; @@ -25,8 +25,8 @@ import com.yahoo.vespa.hosted.controller.api.identifiers.PropertyId; import com.yahoo.vespa.hosted.controller.api.identifiers.ScrewdriverId; import com.yahoo.vespa.hosted.controller.api.identifiers.UserId; import com.yahoo.vespa.hosted.controller.api.integration.MetricsService.ApplicationMetrics; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.ApplicationAction; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.HostedAthenzIdentities; +import com.yahoo.vespa.hosted.controller.athenz.ApplicationAction; +import com.yahoo.vespa.hosted.controller.athenz.HostedAthenzIdentities; import com.yahoo.vespa.hosted.controller.api.integration.configserver.ConfigServerException; import com.yahoo.vespa.hosted.controller.api.integration.deployment.JobType; import com.yahoo.vespa.hosted.controller.api.integration.organization.IssueId; @@ -110,7 +110,7 @@ public class ApplicationApiTest extends ControllerContainerTest { private static final ScrewdriverId SCREWDRIVER_ID = new ScrewdriverId("12345"); private static final UserId USER_ID = new UserId("myuser"); private static final UserId HOSTED_VESPA_OPERATOR = new UserId("johnoperator"); - private static final NToken N_TOKEN = new NToken("dummy"); + private static final OktaAccessToken OKTA_AT = new OktaAccessToken("dummy"); private static final ZoneId TEST_ZONE = ZoneId.from(Environment.test, RegionName.from("us-east-1")); private static final ZoneId STAGING_ZONE = ZoneId.from(Environment.staging, RegionName.from("us-east-3")); @@ -146,12 +146,12 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant1", POST) .userIdentity(USER_ID) .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), new File("tenant-without-applications.json")); // PUT (modify) a tenant tester.assertResponse(request("/application/v4/tenant/tenant1", PUT) .userIdentity(USER_ID) - .nToken(N_TOKEN) + .oktaAccessToken(OKTA_AT) .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), new File("tenant-without-applications.json")); // GET the authenticated user (with associated tenants) @@ -170,13 +170,13 @@ public class ApplicationApiTest extends ControllerContainerTest { // POST (add) a tenant with property ID tester.assertResponse(request("/application/v4/tenant/tenant2", POST) .userIdentity(USER_ID) - .nToken(N_TOKEN) + .oktaAccessToken(OKTA_AT) .data("{\"athensDomain\":\"domain2\", \"property\":\"property2\", \"propertyId\":\"1234\"}"), new File("tenant-without-applications-with-id.json")); // PUT (modify) a tenant with property ID tester.assertResponse(request("/application/v4/tenant/tenant2", PUT) .userIdentity(USER_ID) - .nToken(N_TOKEN) + .oktaAccessToken(OKTA_AT) .data("{\"athensDomain\":\"domain2\", \"property\":\"property2\", \"propertyId\":\"1234\"}"), new File("tenant-without-applications-with-id.json")); // GET a tenant with property ID and contact information @@ -187,7 +187,7 @@ public class ApplicationApiTest extends ControllerContainerTest { // POST (create) an application tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", POST) .userIdentity(USER_ID) - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), new File("application-reference.json")); // GET a tenant tester.assertResponse(request("/application/v4/tenant/tenant1", GET).userIdentity(USER_ID), @@ -267,7 +267,7 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2", POST) .userIdentity(USER_ID) - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), new File("application-reference-2.json")); ApplicationId app2 = ApplicationId.from("tenant2", "application2", "default"); @@ -293,7 +293,7 @@ public class ApplicationApiTest extends ControllerContainerTest { // DELETE application tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2", DELETE) .userIdentity(USER_ID) - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), ""); // GET tenant screwdriver projects @@ -445,11 +445,11 @@ public class ApplicationApiTest extends ControllerContainerTest { // DELETE an application tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", DELETE).userIdentity(USER_ID) - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), ""); // DELETE a tenant tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE).userIdentity(USER_ID) - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), new File("tenant-without-applications.json")); } @@ -521,13 +521,13 @@ public class ApplicationApiTest extends ControllerContainerTest { // Create tenant tester.assertResponse(request("/application/v4/tenant/tenant1", POST).userIdentity(USER_ID) .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), new File("tenant-without-applications.json")); // Create application tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", POST) .userIdentity(USER_ID) - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), new File("application-reference.json")); // Grant deploy access @@ -662,21 +662,21 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant1", POST) .userIdentity(USER_ID) .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), new File("tenant-without-applications.json")); // POST (add) another tenant under the same domain tester.assertResponse(request("/application/v4/tenant/tenant2", POST) .userIdentity(USER_ID) .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Could not create tenant 'tenant2': The Athens domain 'domain1' is already connected to tenant 'tenant1'\"}", 400); // Add the same tenant again tester.assertResponse(request("/application/v4/tenant/tenant1", POST) .userIdentity(USER_ID) - .nToken(N_TOKEN) + .oktaAccessToken(OKTA_AT) .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Tenant 'tenant1' already exists\"}", 400); @@ -685,7 +685,7 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/my_tenant_2", POST) .userIdentity(USER_ID) .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), "{\"error-code\":\"BAD_REQUEST\",\"message\":\"New tenant or application names must start with a letter, may contain no more than 20 characters, and may only contain lowercase letters, digits or dashes, but no double-dashes.\"}", 400); @@ -693,14 +693,14 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/by-tenant2", POST) .userIdentity(USER_ID) .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Athenz tenant name cannot have prefix 'by-'\"}", 400); // POST (create) an (empty) application tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", POST) .userIdentity(USER_ID) - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), new File("application-reference.json")); // Create the same application again @@ -743,14 +743,14 @@ public class ApplicationApiTest extends ControllerContainerTest { // DELETE tenant which has an application tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE) .userIdentity(USER_ID) - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Could not delete tenant 'tenant1': This tenant has active applications\"}", 400); // DELETE application tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", DELETE) .userIdentity(USER_ID) - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), ""); // DELETE application again - should produce 404 tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", DELETE) @@ -760,7 +760,7 @@ public class ApplicationApiTest extends ControllerContainerTest { // DELETE tenant tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE) .userIdentity(USER_ID) - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), new File("tenant-without-applications.json")); // DELETE tenant again - should produce 404 tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE) @@ -777,12 +777,12 @@ public class ApplicationApiTest extends ControllerContainerTest { // Create legancy tenant name containing underscores tester.controller().tenants().create(new AthenzTenant(TenantName.from("my_tenant"), ATHENZ_TENANT_DOMAIN, new Property("property1"), Optional.empty(), Optional.empty()), - N_TOKEN); + OKTA_AT); // POST (add) a Athenz tenant with dashes duplicates existing one with underscores tester.assertResponse(request("/application/v4/tenant/my-tenant", POST) .userIdentity(USER_ID) .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Tenant 'my-tenant' already exists\"}", 400); } @@ -818,21 +818,21 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant1", POST) .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") .userIdentity(authorizedUser) - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), new File("tenant-without-applications.json"), 200); // Creating an application for an Athens domain the user is not admin for is disallowed tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", POST) .userIdentity(unauthorizedUser) - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), "{\n \"code\" : 403,\n \"message\" : \"Tenant admin or Vespa operator role required\"\n}", 403); // (Create it with the right tenant id) tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", POST) .userIdentity(authorizedUser) - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), new File("application-reference.json"), 200); @@ -853,7 +853,7 @@ public class ApplicationApiTest extends ControllerContainerTest { // (Deleting it with the right tenant id) tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", DELETE) .userIdentity(authorizedUser) - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), "", 200); @@ -869,7 +869,7 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant1", PUT) .data("{\"athensDomain\":\"domain2\", \"property\":\"property1\"}") .userIdentity(authorizedUser) - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), "{\"tenant\":\"tenant1\",\"type\":\"ATHENS\",\"athensDomain\":\"domain2\",\"property\":\"property1\",\"applications\":[]}", 200); @@ -1084,7 +1084,7 @@ public class ApplicationApiTest extends ControllerContainerTest { private final Request.Method method; private byte[] data = new byte[0]; private AthenzIdentity identity; - private NToken nToken; + private OktaAccessToken oktaAccessToken; private String contentType = "application/json"; private String recursive; @@ -1106,7 +1106,7 @@ public class ApplicationApiTest extends ControllerContainerTest { } private RequestBuilder userIdentity(UserId userId) { this.identity = HostedAthenzIdentities.from(userId); return this; } private RequestBuilder screwdriverIdentity(ScrewdriverId screwdriverId) { this.identity = HostedAthenzIdentities.from(screwdriverId); return this; } - private RequestBuilder nToken(NToken nToken) { this.nToken = nToken; return this; } + private RequestBuilder oktaAccessToken(OktaAccessToken oktaAccessToken) { this.oktaAccessToken = oktaAccessToken; return this; } private RequestBuilder contentType(String contentType) { this.contentType = contentType; return this; } private RequestBuilder recursive(String recursive) { this.recursive = recursive; return this; } @@ -1120,8 +1120,8 @@ public class ApplicationApiTest extends ControllerContainerTest { if (identity != null) { addIdentityToRequest(request, identity); } - if (nToken != null) { - addNTokenToRequest(request, nToken); + if (oktaAccessToken != null) { + addOktaAccessToken(request, oktaAccessToken); } return request; } @@ -1164,11 +1164,11 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant1", POST) .userIdentity(USER_ID) .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), new File("tenant-without-applications.json")); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", POST) .userIdentity(USER_ID) - .nToken(N_TOKEN), + .oktaAccessToken(OKTA_AT), new File("application-reference.json")); addScrewdriverUserToDeployRole(SCREWDRIVER_ID, ATHENZ_TENANT_DOMAIN, new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId("application1")); 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 9951e4499f7..c3b9c11de88 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 @@ -14,8 +14,8 @@ 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.integration.athenz.ApplicationAction; -import com.yahoo.vespa.hosted.controller.api.integration.athenz.HostedAthenzIdentities; +import com.yahoo.vespa.hosted.controller.athenz.ApplicationAction; +import com.yahoo.vespa.hosted.controller.athenz.HostedAthenzIdentities; 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; -- cgit v1.2.3