summaryrefslogtreecommitdiffstats
path: root/controller-server
diff options
context:
space:
mode:
authorMorten Tokle <morten.tokle@gmail.com>2018-10-25 08:59:00 +0200
committerGitHub <noreply@github.com>2018-10-25 08:59:00 +0200
commitd39518cec5b3b0202efbcc8a9a22527136f83a3c (patch)
tree9f5442d90fa63fbd0a4249f11b4f0086a21a48b3 /controller-server
parent9a91a16f12a00eae938b349b44fce589451ed3b2 (diff)
parentd3d43177c8bc9a5a3a376f5eaf2384edcfc7e285 (diff)
Merge pull request #7436 from vespa-engine/bjorncs/athenz-okta-tenancy
Bjorncs/athenz okta tenancy
Diffstat (limited to 'controller-server')
-rw-r--r--controller-server/pom.xml44
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/ApplicationController.java24
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/Controller.java7
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/TenantController.java36
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/ApplicationAction.java17
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/HostedAthenzIdentities.java27
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/UserAuthWithAthenzPrincipalFilter.java104
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzClientFactoryImpl.java64
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsClientFacade.java122
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/ZmsClientImpl.java191
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzClientFactoryMock.java20
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/AthenzDbMock.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/mock/ZmsClientMock.java117
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java37
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilter.java25
-rw-r--r--controller-server/src/main/resources/configdefinitions/athenz.def29
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java5
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/TestIdentities.java25
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/AthenzFilterMock.java8
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/DnsMaintainerTest.java4
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/JobRunnerTest.java6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java10
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java7
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java74
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/filter/ControllerAuthorizationFilterTest.java4
26 files changed, 381 insertions, 632 deletions
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 @@
<version>1.6</version>
</dependency>
- <dependency>
- <groupId>com.yahoo.athenz</groupId>
- <artifactId>athenz-zms-java-client</artifactId>
- <scope>compile</scope>
- <exclusions>
- <exclusion>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-api</artifactId>
- </exclusion>
- <!-- Exclude all Jersey bundles provided by JDisc -->
- <exclusion>
- <groupId>org.glassfish.jersey.core</groupId>
- <artifactId>jersey-client</artifactId>
- </exclusion>
- <exclusion>
- <groupId>org.glassfish.jersey.media</groupId>
- <artifactId>jersey-media-json-jackson</artifactId>
- </exclusion>
- <!-- BouncyCastle is not bundled due to class loading issues
- when security provider is registered from inside a OSGi bundle -->
- <exclusion>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcpkix-jdk15on</artifactId>
- </exclusion>
- <exclusion>
- <groupId>org.bouncycastle</groupId>
- <artifactId>bcprov-jdk15on</artifactId>
- </exclusion>
- <!--Exclude all Jackson bundles provided by JDisc -->
- <exclusion>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-core</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- </exclusion>
- <exclusion>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-annotations</artifactId>
- </exclusion>
- </exclusions>
- </dependency>
-
<!-- test -->
<dependency>
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 21afe1b0778..bc48051c111 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.ConfigServerException;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Log;
@@ -44,6 +43,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;
@@ -94,7 +94,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;
@@ -109,7 +109,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;
@@ -251,7 +251,7 @@ public class ApplicationController {
*
* @throws IllegalArgumentException if the application already exists
*/
- public Application createApplication(ApplicationId id, Optional<NToken> token) {
+ public Application createApplication(ApplicationId id, Optional<OktaAccessToken> 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())
@@ -270,11 +270,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);
@@ -532,7 +531,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<NToken> token) {
+ public void deleteApplication(ApplicationId applicationId, Optional<OktaAccessToken> token) {
// Find all instances of the application
List<ApplicationId> instances = asList(applicationId.tenant()).stream()
.map(Application::id)
@@ -549,13 +548,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<AthenzDomain> 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<Tenant> asList(UserId user) {
AthenzUser athenzUser = AthenzUser.fromUserId(user.id());
- try (ZtsClient ztsClient = athenzClientFactory.createZtsClientWithServicePrincipal()) {
- Set<AthenzDomain> userDomains = new HashSet<>(ztsClient.getTenantDomains(athenzClientFactory.getControllerIdentity(), athenzUser, "admin"));
+ Set<AthenzDomain> 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<Tenant> 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<Application> 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/filter/UserAuthWithAthenzPrincipalFilter.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/UserAuthWithAthenzPrincipalFilter.java
deleted file mode 100644
index 26cd9f2e9b8..00000000000
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/filter/UserAuthWithAthenzPrincipalFilter.java
+++ /dev/null
@@ -1,104 +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.filter;
-
-import com.google.inject.Inject;
-import com.yahoo.jdisc.Response;
-import com.yahoo.jdisc.http.filter.DiscFilterRequest;
-import com.yahoo.jdisc.http.filter.security.athenz.AthenzPrincipalFilter;
-import com.yahoo.jdisc.http.filter.security.athenz.AthenzPrincipalFilterConfig;
-import com.yahoo.jdisc.http.filter.security.cors.CorsFilterConfig;
-import com.yahoo.log.LogLevel;
-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.hosted.controller.api.identifiers.UserId;
-import com.yahoo.vespa.hosted.controller.athenz.config.AthenzConfig;
-import com.yahoo.yolean.chain.After;
-
-import java.security.Principal;
-import java.util.Optional;
-import java.util.logging.Logger;
-import java.util.stream.Stream;
-
-
-/**
- * A variant of the {@link AthenzPrincipalFilter} to be used in combination with a cookie-based
- * security filter for user authentication
- * Assumes that the user authentication filter configured in the same filter chain and is configured to run before this filter.
- *
- * @author bjorncs
- */
-// TODO Remove this filter once migrated to Okta
-@After({"CorsPreflightRequestFilter", "BouncerFilter"})
-public class UserAuthWithAthenzPrincipalFilter extends AthenzPrincipalFilter {
-
- private static final Logger log = Logger.getLogger(UserAuthWithAthenzPrincipalFilter.class.getName());
-
- private final String userAuthenticationPassThruAttribute;
- private final String principalHeaderName;
-
- @Inject
- public UserAuthWithAthenzPrincipalFilter(AthenzPrincipalFilterConfig filterConfig, AthenzConfig athenzConfig, CorsFilterConfig corsConfig) {
- super(filterConfig, corsConfig);
- this.userAuthenticationPassThruAttribute = athenzConfig.userAuthenticationPassThruAttribute();
- this.principalHeaderName = filterConfig.principalHeaderName();
- }
-
- @Override
- public Optional<ErrorResponse> filterRequest(DiscFilterRequest request) {
- if (request.getMethod().equals("OPTIONS")) return Optional.empty(); // Skip authentication on OPTIONS - required for Javascript CORS
-
- try {
- switch (getUserAuthenticationResult(request)) {
- case USER_COOKIE_MISSING:
- case USER_COOKIE_ALTERNATIVE_MISSING:
- return super.filterRequest(request); // Cookie-based authentication failed, delegate to Athenz
- case USER_COOKIE_OK:
- rewriteUserPrincipalToAthenz(request);
- return Optional.empty(); // Authenticated using user cookie
- case USER_COOKIE_INVALID:
- return Optional.of(new ErrorResponse(Response.Status.UNAUTHORIZED, "Your user cookie is invalid (either expired, tampered or invalid ip)"));
- default:
- return Optional.empty();
- }
- } catch (Exception e) {
- log.log(LogLevel.WARNING, "Authentication failed: " + e.getMessage(), e);
- return Optional.of(new ErrorResponse(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage()));
- }
- }
-
- private UserAuthenticationResult getUserAuthenticationResult(DiscFilterRequest request) {
- if (!request.containsAttribute(userAuthenticationPassThruAttribute)) {
- throw new IllegalStateException("User authentication filter passthru attribute missing");
- }
- Integer statusCode = (Integer) request.getAttribute(userAuthenticationPassThruAttribute);
- return Stream.of(UserAuthenticationResult.values())
- .filter(uar -> uar.statusCode == statusCode)
- .findAny()
- .orElseThrow(() -> new IllegalStateException("Invalid status code: " + statusCode));
- }
-
- private void rewriteUserPrincipalToAthenz(DiscFilterRequest request) {
- Principal userPrincipal = request.getUserPrincipal();
- log.log(LogLevel.DEBUG, () -> "Original user principal: " + userPrincipal.toString());
- UserId userId = new UserId(userPrincipal.getName());
- AthenzUser athenzIdentity = AthenzUser.fromUserId(userId.id());
- request.setRemoteUser(athenzIdentity.getFullName());
- NToken nToken = Optional.ofNullable(request.getHeader(principalHeaderName)).map(NToken::new).orElse(null);
- request.setUserPrincipal(new AthenzPrincipal(athenzIdentity, nToken));
- }
-
- private enum UserAuthenticationResult {
- USER_COOKIE_MISSING(0),
- USER_COOKIE_OK(1),
- USER_COOKIE_INVALID(-1),
- USER_COOKIE_ALTERNATIVE_MISSING(-2);
-
- final int statusCode;
-
- UserAuthenticationResult(int statusCode) {
- this.statusCode = statusCode;
- }
-
- }
-}
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<RoleAction> 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<AthenzDomain> getDomainList(String prefix) {
+ log.log(LogLevel.DEBUG, String.format("getDomainList(prefix=%s)", prefix));
+ return zmsClient.getDomainList(prefix);
+ }
+
+ private static Set<RoleAction> 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<TenantRoleAction> 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<AthenzDomain> 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<TenantRoleAction> createTenantRoleActions() {
- return Arrays.stream(ApplicationAction.values())
- .map(action -> new TenantRoleAction().setAction(action.name()).setRole(action.roleName))
- .collect(toList());
- }
-
- private static List<AthenzDomain> toAthenzDomains(List<String> 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> T getOrThrow(Supplier<T> 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\\.(?<tenantDomain>[\\w\\-_]+)\\..*");
+ private static final Pattern APPLICATION_RESOURCE_PATTERN = Pattern.compile("service\\.hosting\\.tenant\\.[\\w\\-_]+\\.res_group\\.(?<resourceGroup>[\\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<RoleAction> 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<AthenzDomain> 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<AthenzDomain> 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 4da1e6d1f58..bcf0ff70c64 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
@@ -688,7 +689,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()));
@@ -715,17 +716,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;
@@ -841,7 +842,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 {
@@ -855,7 +856,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
}
@@ -1032,8 +1033,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()));
@@ -1225,9 +1225,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<OktaAccessToken> 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 378dc0315d6..59847437339 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<String> allowedUrls) {
super(allowedUrls);
- this.clientFactory = clientFactory;
+ this.zmsClient = new ZmsClientFacade(clientFactory.createZmsClient(), clientFactory.getControllerIdentity());;
this.tenantController = tenantController;
}
@@ -152,8 +153,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) {
@@ -167,8 +167,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;
@@ -211,13 +210,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/main/resources/configdefinitions/athenz.def b/controller-server/src/main/resources/configdefinitions/athenz.def
index f8d65c25e47..dc1a2337aaf 100644
--- a/controller-server/src/main/resources/configdefinitions/athenz.def
+++ b/controller-server/src/main/resources/configdefinitions/athenz.def
@@ -1,44 +1,15 @@
# Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
namespace=vespa.hosted.controller.athenz.config
-# Principal header name
-principalHeaderName string default="Athenz-Principal-Auth"
-# TODO Remove once migrated to Okta
-
# URL to ZMS API endpoint
zmsUrl string
-# URL to legacy ZMS API endpoint
-legacyZmsUrl string
-# TODO Remove once migrated to Okta
-
# URL to ZTS API endpoint
ztsUrl string
# Athenz domain for controller identity. The domain is also used for Athenz tenancy integration.
domain string
-# Name of the internal user authentication passthru attribute
-userAuthenticationPassThruAttribute string
-# TODO Remove once migrated to Okta
-
-# Path to Athenz CA JKS trust store
-athenzCaTrustStore string
-
-# Certificate DNS domain
-certDnsDomain string
-
# Athenz service name for controller identity
service.name string
-# Athenz service public key id
-service.publicKeyId string
-
-# Version of Athenz service private key
-service.privateKeyVersion int
-
-# Name of Athenz service private key secret
-service.privateKeySecretName string
-
-# Expiry of service principal token and certificate
-service.credentialsExpiryMinutes int default=43200 # 30 days
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 1bec06ea0e0..3351efff2dd 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 13ac5c41644..ca31eb52979 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 ab00ecab376..027fc9745f2 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
@@ -454,11 +454,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"));
}
@@ -530,13 +530,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
@@ -671,21 +671,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);
@@ -694,7 +694,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);
@@ -702,14 +702,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
@@ -752,14 +752,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)
@@ -769,7 +769,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)
@@ -786,12 +786,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);
}
@@ -827,21 +827,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);
@@ -862,7 +862,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);
@@ -878,7 +878,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);
@@ -1093,7 +1093,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;
@@ -1115,7 +1115,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; }
@@ -1129,8 +1129,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;
}
@@ -1173,11 +1173,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;