aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClientMock.java13
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java26
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/AthenzAccessControlRequests.java17
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/AthenzCredentials.java20
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTester.java7
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/AthenzFilterMock.java3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ContainerControllerTester.java4
-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.java107
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/OktaIdentityToken.java40
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/DefaultZmsClient.java24
-rw-r--r--vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/ZmsClient.java15
12 files changed, 187 insertions, 96 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClientMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClientMock.java
index 84aa1033e65..a80843ad252 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClientMock.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/athenz/ZmsClientMock.java
@@ -6,6 +6,7 @@ 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.OktaAccessToken;
+import com.yahoo.vespa.athenz.api.OktaIdentityToken;
import com.yahoo.vespa.athenz.client.zms.RoleAction;
import com.yahoo.vespa.athenz.client.zms.ZmsClient;
import com.yahoo.vespa.athenz.client.zms.ZmsClientException;
@@ -38,13 +39,15 @@ public class ZmsClientMock implements ZmsClient {
}
@Override
- public void createTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, OktaAccessToken token) {
+ public void createTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService,
+ OktaIdentityToken identityToken, OktaAccessToken accessToken) {
log("createTenancy(tenantDomain='%s')", tenantDomain);
getDomainOrThrow(tenantDomain, false).isVespaTenant = true;
}
@Override
- public void deleteTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, OktaAccessToken token) {
+ public void deleteTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService,
+ OktaIdentityToken identityToken, OktaAccessToken accessToken) {
log("deleteTenancy(tenantDomain='%s')", tenantDomain);
AthenzDbMock.Domain domain = getDomainOrThrow(tenantDomain, false);
domain.isVespaTenant = false;
@@ -53,7 +56,8 @@ public class ZmsClientMock implements ZmsClient {
}
@Override
- public void createProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup, Set<RoleAction> roleActions, OktaAccessToken token) {
+ public void createProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup,
+ Set<RoleAction> roleActions, OktaIdentityToken identityToken, OktaAccessToken accessToken) {
log("createProviderResourceGroup(tenantDomain='%s', resourceGroup='%s')", tenantDomain, resourceGroup);
AthenzDbMock.Domain domain = getDomainOrThrow(tenantDomain, true);
ApplicationId applicationId = new ApplicationId(resourceGroup);
@@ -63,7 +67,8 @@ public class ZmsClientMock implements ZmsClient {
}
@Override
- public void deleteProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup, OktaAccessToken token) {
+ public void deleteProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup,
+ OktaIdentityToken identityToken, OktaAccessToken accessToken) {
log("deleteProviderResourceGroup(tenantDomain='%s', resourceGroup='%s')", tenantDomain, resourceGroup);
getDomainOrThrow(tenantDomain, true).applications.remove(new ApplicationId(resourceGroup));
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java
index 304a47044a1..70c504dd220 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/athenz/impl/AthenzFacade.java
@@ -12,6 +12,7 @@ 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.api.OktaIdentityToken;
import com.yahoo.vespa.athenz.client.zms.RoleAction;
import com.yahoo.vespa.athenz.client.zms.ZmsClient;
import com.yahoo.vespa.athenz.client.zms.ZmsClientException;
@@ -85,7 +86,7 @@ public class AthenzFacade implements AccessControl {
}
else { // Create tenant resources in Athenz if domain is not already taken.
log("createTenancy(tenantDomain=%s, service=%s)", domain, service);
- zmsClient.createTenancy(domain, service, athenzCredentials.token());
+ zmsClient.createTenancy(domain, service, athenzCredentials.identityToken(), athenzCredentials.accessToken());
}
return tenant;
@@ -121,14 +122,14 @@ public class AthenzFacade implements AccessControl {
}
else { // Delete and recreate tenant, and optionally application, resources in Athenz otherwise.
log("createTenancy(tenantDomain=%s, service=%s)", newDomain, service);
- zmsClient.createTenancy(newDomain, service, athenzCredentials.token());
+ zmsClient.createTenancy(newDomain, service, athenzCredentials.identityToken(), athenzCredentials.accessToken());
for (Application application : applications)
- createApplication(newDomain, application.id().application(), athenzCredentials.token());
+ createApplication(newDomain, application.id().application(), athenzCredentials.identityToken(), athenzCredentials.accessToken());
log("deleteTenancy(tenantDomain=%s, service=%s)", oldDomain, service);
for (Application application : applications)
- deleteApplication(oldDomain, application.id().application(), athenzCredentials.token());
- zmsClient.deleteTenancy(oldDomain, service, athenzCredentials.token());
+ deleteApplication(oldDomain, application.id().application(), athenzCredentials.identityToken(), athenzCredentials.accessToken());
+ zmsClient.deleteTenancy(oldDomain, service, athenzCredentials.identityToken(), athenzCredentials.accessToken());
}
return tenant;
@@ -139,22 +140,22 @@ public class AthenzFacade implements AccessControl {
AthenzCredentials athenzCredentials = (AthenzCredentials) credentials;
log("deleteTenancy(tenantDomain=%s, service=%s)", athenzCredentials.domain(), service);
- zmsClient.deleteTenancy(athenzCredentials.domain(), service, athenzCredentials.token());
+ zmsClient.deleteTenancy(athenzCredentials.domain(), service, athenzCredentials.identityToken(), athenzCredentials.accessToken());
}
@Override
public void createApplication(TenantAndApplicationId id, Credentials credentials) {
AthenzCredentials athenzCredentials = (AthenzCredentials) credentials;
- createApplication(athenzCredentials.domain(), id.application(), athenzCredentials.token());
+ createApplication(athenzCredentials.domain(), id.application(), athenzCredentials.identityToken(), athenzCredentials.accessToken());
}
- private void createApplication(AthenzDomain domain, ApplicationName application, OktaAccessToken token) {
+ private void createApplication(AthenzDomain domain, ApplicationName application, OktaIdentityToken identityToken, OktaAccessToken accessToken) {
Set<RoleAction> tenantRoleActions = createTenantRoleActions();
log("createProviderResourceGroup(" +
"tenantDomain=%s, providerDomain=%s, service=%s, resourceGroup=%s, roleActions=%s)",
domain, service.getDomain().getName(), service.getName(), application, tenantRoleActions);
try {
- zmsClient.createProviderResourceGroup(domain, service, application.value(), tenantRoleActions, token);
+ zmsClient.createProviderResourceGroup(domain, service, application.value(), tenantRoleActions, identityToken, accessToken);
}
catch (ZmsClientException e) {
if (e.getErrorCode() == com.yahoo.jdisc.Response.Status.FORBIDDEN)
@@ -169,7 +170,8 @@ public class AthenzFacade implements AccessControl {
AthenzCredentials athenzCredentials = (AthenzCredentials) credentials;
log("deleteProviderResourceGroup(tenantDomain=%s, providerDomain=%s, service=%s, resourceGroup=%s)",
athenzCredentials.domain(), service.getDomain().getName(), service.getName(), id.application());
- zmsClient.deleteProviderResourceGroup(athenzCredentials.domain(), service, id.application().value(), athenzCredentials.token());
+ zmsClient.deleteProviderResourceGroup(athenzCredentials.domain(), service, id.application().value(),
+ athenzCredentials.identityToken(), athenzCredentials.accessToken());
}
@Override
@@ -182,10 +184,10 @@ public class AthenzFacade implements AccessControl {
.collect(Collectors.toUnmodifiableList());
}
- private void deleteApplication(AthenzDomain domain, ApplicationName application, OktaAccessToken token) {
+ private void deleteApplication(AthenzDomain domain, ApplicationName application, OktaIdentityToken identityToken, OktaAccessToken accessToken) {
log("deleteProviderResourceGroup(tenantDomain=%s, providerDomain=%s, service=%s, resourceGroup=%s)",
domain, service.getDomain().getName(), service.getName(), application);
- zmsClient.deleteProviderResourceGroup(domain, service, application.value(), token);
+ zmsClient.deleteProviderResourceGroup(domain, service, application.value(), identityToken, accessToken);
}
public boolean hasApplicationAccess(
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/AthenzAccessControlRequests.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/AthenzAccessControlRequests.java
index e658d65248e..38faab1483d 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/AthenzAccessControlRequests.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/AthenzAccessControlRequests.java
@@ -7,6 +7,7 @@ import com.yahoo.slime.Inspector;
import com.yahoo.vespa.athenz.api.AthenzDomain;
import com.yahoo.vespa.athenz.api.AthenzPrincipal;
import com.yahoo.vespa.athenz.api.OktaAccessToken;
+import com.yahoo.vespa.athenz.api.OktaIdentityToken;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.TenantController;
import com.yahoo.vespa.hosted.controller.api.identifiers.Property;
@@ -16,6 +17,7 @@ import com.yahoo.vespa.hosted.controller.tenant.AthenzTenant;
import java.security.Principal;
import java.util.Objects;
import java.util.Optional;
+import java.util.function.Function;
/**
* Extracts access control data for Athenz or user tenants from HTTP requests.
@@ -44,13 +46,22 @@ public class AthenzAccessControlRequests implements AccessControlRequests {
return new AthenzCredentials(requireAthenzPrincipal(request),
tenants.get(tenant).map(AthenzTenant.class::cast).map(AthenzTenant::domain)
.orElseGet(() -> new AthenzDomain(required("athensDomain", requestObject))),
+ requireOktaIdentityToken(request),
requireOktaAccessToken(request));
}
+ private static OktaIdentityToken requireOktaIdentityToken(HttpRequest request) {
+ return requireToken(request, OktaIdentityToken::new, "okta.identity-token", "No Okta Identity Token provided");
+ }
+
private static OktaAccessToken requireOktaAccessToken(HttpRequest request) {
- return Optional.ofNullable(request.context().get("okta.access-token"))
- .map(attribute -> new OktaAccessToken((String) attribute))
- .orElseThrow(() -> new IllegalArgumentException("No Okta Access Token provided"));
+ return requireToken(request, OktaAccessToken::new, "okta.access-token", "No Okta Access Token provided");
+ }
+
+ private static <T> T requireToken(HttpRequest request, Function<String, T> tokenFactory, String attribute, String errorMessage) {
+ return Optional.ofNullable(request.context().get(attribute))
+ .map(value -> tokenFactory.apply((String) value))
+ .orElseThrow(() -> new IllegalArgumentException(errorMessage));
}
private static String required(String fieldName, Inspector object) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/AthenzCredentials.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/AthenzCredentials.java
index c9e3e249668..57c9e037f6a 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/AthenzCredentials.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/security/AthenzCredentials.java
@@ -3,8 +3,7 @@ package com.yahoo.vespa.hosted.controller.security;
import com.yahoo.vespa.athenz.api.AthenzDomain;
import com.yahoo.vespa.athenz.api.AthenzPrincipal;
import com.yahoo.vespa.athenz.api.OktaAccessToken;
-
-import java.util.Optional;
+import com.yahoo.vespa.athenz.api.OktaIdentityToken;
import static java.util.Objects.requireNonNull;
@@ -18,12 +17,15 @@ import static java.util.Objects.requireNonNull;
public class AthenzCredentials extends Credentials {
private final AthenzDomain domain;
- private final OktaAccessToken token;
+ private final OktaIdentityToken identityToken;
+ private final OktaAccessToken accessToken;
- public AthenzCredentials(AthenzPrincipal user, AthenzDomain domain, OktaAccessToken token) {
+ public AthenzCredentials(AthenzPrincipal user, AthenzDomain domain,
+ OktaIdentityToken identityToken, OktaAccessToken accessToken) {
super(user);
this.domain = requireNonNull(domain);
- this.token = requireNonNull(token);
+ this.accessToken = requireNonNull(accessToken);
+ this.identityToken = requireNonNull(identityToken);
}
@Override
@@ -32,7 +34,11 @@ public class AthenzCredentials extends Credentials {
/** Returns the Athenz domain of the tenant on whose behalf this request is made. */
public AthenzDomain domain() { return domain; }
- /** Returns the token proving access to the requested action under this domain. */
- public OktaAccessToken token() { return token; }
+ /** Returns the Okta access token required for Athenz tenancy operation */
+ public OktaAccessToken accessToken() { return accessToken; }
+
+ /** /** Returns the Okta identity token required for Athenz tenancy operation */
+ public OktaIdentityToken identityToken() { return identityToken; }
+
}
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 6e204a418ad..a3b2861b387 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
@@ -13,6 +13,7 @@ import com.yahoo.vespa.athenz.api.AthenzDomain;
import com.yahoo.vespa.athenz.api.AthenzPrincipal;
import com.yahoo.vespa.athenz.api.AthenzUser;
import com.yahoo.vespa.athenz.api.OktaAccessToken;
+import com.yahoo.vespa.athenz.api.OktaIdentityToken;
import com.yahoo.vespa.flags.InMemoryFlagSource;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions;
import com.yahoo.vespa.hosted.controller.api.identifiers.Property;
@@ -258,7 +259,8 @@ public final class ControllerTester {
domain,
new Property("Property" + propertyId),
Optional.ofNullable(propertyId).map(Object::toString).map(PropertyId::new));
- AthenzCredentials credentials = new AthenzCredentials(new AthenzPrincipal(user), domain, new OktaAccessToken("okta-token"));
+ AthenzCredentials credentials = new AthenzCredentials(
+ new AthenzPrincipal(user), domain, new OktaIdentityToken("okta-identity-token"), new OktaAccessToken("okta-access-token"));
controller().tenants().create(tenantSpec, credentials);
if (contact.isPresent())
controller().tenants().lockOrThrow(name, LockedTenant.Athenz.class, tenant ->
@@ -279,7 +281,8 @@ public final class ControllerTester {
public Optional<Credentials> credentialsFor(TenantAndApplicationId id) {
return domainOf(id).map(domain -> new AthenzCredentials(new AthenzPrincipal(new AthenzUser("user")),
domain,
- new OktaAccessToken("okta-token")));
+ new OktaIdentityToken("okta-identity-token"),
+ new OktaAccessToken("okta-access-token")));
}
public Application createApplication(TenantName tenant, String applicationName, String instanceName) {
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 8eb64a00d40..6cc6ca012c7 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
@@ -25,6 +25,7 @@ import java.util.Optional;
public class AthenzFilterMock implements SecurityRequestFilter {
public static final String IDENTITY_HEADER_NAME = "Athenz-Identity";
+ public static final String OKTA_IDENTITY_TOKEN_HEADER_NAME = "Okta-Identity-Token";
public static final String OKTA_ACCESS_TOKEN_HEADER_NAME = "Okta-Access-Token";
private static final ObjectMapper mapper = new ObjectMapper();
@@ -47,6 +48,8 @@ public class AthenzFilterMock implements SecurityRequestFilter {
AthenzPrincipal principal = new AthenzPrincipal(identity);
request.setUserPrincipal(principal);
}
+ Optional.ofNullable(request.getHeader(OKTA_IDENTITY_TOKEN_HEADER_NAME))
+ .ifPresent(header -> request.setAttribute("okta.identity-token", header));
Optional.ofNullable(request.getHeader(OKTA_ACCESS_TOKEN_HEADER_NAME))
.ifPresent(header -> request.setAttribute("okta.access-token", header));
}
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 912d285fb52..110aaf2b1a6 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
@@ -11,6 +11,7 @@ import com.yahoo.vespa.athenz.api.AthenzDomain;
import com.yahoo.vespa.athenz.api.AthenzPrincipal;
import com.yahoo.vespa.athenz.api.AthenzUser;
import com.yahoo.vespa.athenz.api.OktaAccessToken;
+import com.yahoo.vespa.athenz.api.OktaIdentityToken;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.Controller;
import com.yahoo.vespa.hosted.controller.api.application.v4.model.DeployOptions;
@@ -72,7 +73,8 @@ public class ContainerControllerTester {
public Application createApplication(String athensDomain, String tenant, String application, String instance) {
AthenzDomain domain1 = addTenantAthenzDomain(athensDomain, "user");
AthenzPrincipal user = new AthenzPrincipal(new AthenzUser("user"));
- AthenzCredentials credentials = new AthenzCredentials(user, domain1, new OktaAccessToken("okta-token"));
+ AthenzCredentials credentials = new AthenzCredentials(
+ user, domain1, new OktaIdentityToken("okta-identity-token"), new OktaAccessToken("okta-access-token"));
AthenzTenantSpec tenantSpec = new AthenzTenantSpec(TenantName.from(tenant),
domain1,
new Property("property1"),
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 f08dc18a58b..fb0e92ab7f4 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
@@ -9,6 +9,7 @@ import com.yahoo.config.provision.SystemName;
import com.yahoo.vespa.athenz.api.AthenzIdentity;
import com.yahoo.vespa.athenz.api.AthenzUser;
import com.yahoo.vespa.athenz.api.OktaAccessToken;
+import com.yahoo.vespa.athenz.api.OktaIdentityToken;
import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactoryMock;
import org.junit.After;
import org.junit.Before;
@@ -18,6 +19,7 @@ import java.nio.charset.CharacterCodingException;
import static com.yahoo.vespa.hosted.controller.integration.AthenzFilterMock.IDENTITY_HEADER_NAME;
import static com.yahoo.vespa.hosted.controller.integration.AthenzFilterMock.OKTA_ACCESS_TOKEN_HEADER_NAME;
+import static com.yahoo.vespa.hosted.controller.integration.AthenzFilterMock.OKTA_IDENTITY_TOKEN_HEADER_NAME;
import static org.junit.Assert.assertEquals;
/**
@@ -154,6 +156,11 @@ public class ControllerContainerTest {
return request;
}
+ protected static Request addOktaIdentityToken(Request request, OktaIdentityToken token) {
+ request.getHeaders().put(OKTA_IDENTITY_TOKEN_HEADER_NAME, token.token());
+ return request;
+ }
+
protected static Request addOktaAccessToken(Request request, OktaAccessToken token) {
request.getHeaders().put(OKTA_ACCESS_TOKEN_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 eac60b72c6f..f7af8ff4ce4 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
@@ -21,6 +21,7 @@ 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.OktaAccessToken;
+import com.yahoo.vespa.athenz.api.OktaIdentityToken;
import com.yahoo.vespa.config.SlimeUtils;
import com.yahoo.vespa.hosted.controller.Application;
import com.yahoo.vespa.hosted.controller.Instance;
@@ -47,7 +48,6 @@ import com.yahoo.vespa.hosted.controller.api.integration.resource.MockTenantCost
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceAllocation;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot;
import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingEndpoint;
-import com.yahoo.vespa.hosted.controller.api.integration.routing.RoutingGeneratorMock;
import com.yahoo.vespa.hosted.controller.api.integration.stubs.MockMeteringClient;
import com.yahoo.vespa.hosted.controller.application.ApplicationPackage;
import com.yahoo.vespa.hosted.controller.application.Change;
@@ -85,7 +85,6 @@ import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.Instant;
-import java.time.LocalDate;
import java.time.YearMonth;
import java.util.ArrayList;
import java.util.Base64;
@@ -151,7 +150,8 @@ public class ApplicationApiTest extends ControllerContainerTest {
private static final UserId USER_ID = new UserId("myuser");
private static final UserId OTHER_USER_ID = new UserId("otheruser");
private static final UserId HOSTED_VESPA_OPERATOR = new UserId("johnoperator");
- private static final OktaAccessToken OKTA_AT = new OktaAccessToken("dummy");
+ private static final OktaIdentityToken OKTA_IT = new OktaIdentityToken("okta-it");
+ private static final OktaAccessToken OKTA_AT = new OktaAccessToken("okta-at");
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"));
@@ -179,12 +179,12 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/tenant1", POST)
.userIdentity(USER_ID)
.data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}")
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
new File("tenant-without-applications.json"));
// PUT (modify) a tenant
tester.assertResponse(request("/application/v4/tenant/tenant1", PUT)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT)
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT)
.data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"),
new File("tenant-without-applications.json"));
// GET the authenticated user (with associated tenants)
@@ -204,11 +204,11 @@ public class ApplicationApiTest extends ControllerContainerTest {
new File("tenant-list.json"));
// GET list of months for a tenant
- tester.assertResponse(request("/application/v4/tenant/tenant1/cost", GET).userIdentity(USER_ID).oktaAccessToken(OKTA_AT),
+ tester.assertResponse(request("/application/v4/tenant/tenant1/cost", GET).userIdentity(USER_ID).oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"months\":[]}");
// GET cost for a month for a tenant
- tester.assertResponse(request("/application/v4/tenant/tenant1/cost/2018-01", GET).userIdentity(USER_ID).oktaAccessToken(OKTA_AT),
+ tester.assertResponse(request("/application/v4/tenant/tenant1/cost/2018-01", GET).userIdentity(USER_ID).oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"month\":\"2018-01\",\"items\":[]}");
// Add another Athens domain, so we can try to create more tenants
@@ -219,13 +219,13 @@ public class ApplicationApiTest extends ControllerContainerTest {
// POST (add) a tenant with property ID
tester.assertResponse(request("/application/v4/tenant/tenant2", POST)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT)
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT)
.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)
- .oktaAccessToken(OKTA_AT)
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT)
.data("{\"athensDomain\":\"domain2\", \"property\":\"property2\", \"propertyId\":\"1234\"}"),
new File("tenant-without-applications-with-id.json"));
// GET a tenant with property ID and contact information
@@ -236,7 +236,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
// POST (create) an application
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", POST)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
new File("instance-reference.json"));
// GET a tenant
tester.assertResponse(request("/application/v4/tenant/tenant1", GET).userIdentity(USER_ID),
@@ -345,7 +345,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/instance/default", POST)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
new File("instance-reference-2.json"));
ApplicationId app2 = ApplicationId.from("tenant2", "application2", "default");
@@ -416,7 +416,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
// DELETE application
tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2", DELETE)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"message\":\"Deleted application tenant2.application2\"}");
// Set version 6.1 to broken to change compile version for.
@@ -577,7 +577,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
// DELETE application with active deployments fails
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", DELETE)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
new File("delete-with-active-deployments.json"), 400);
// DELETE (deactivate) a deployment - dev
@@ -792,24 +792,24 @@ public class ApplicationApiTest extends ControllerContainerTest {
// DELETE all instances under an application to delete the application
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/default", DELETE)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"message\":\"Deleted instance tenant1.application1.default\"}");
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/my-user", DELETE)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"message\":\"Deleted instance tenant1.application1.my-user\"}");
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", DELETE)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"message\":\"Deleted instance tenant1.application1.instance1\"}");
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/otheruser", DELETE)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"message\":\"Deleted instance tenant1.application1.otheruser\"}");
// DELETE a tenant
tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE).userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
new File("tenant-without-applications.json"));
}
@@ -960,13 +960,13 @@ public class ApplicationApiTest extends ControllerContainerTest {
// Create tenant
tester.assertResponse(request("/application/v4/tenant/tenant1", POST).userIdentity(USER_ID)
.data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}")
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
new File("tenant-without-applications.json"));
// Create application
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", POST)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
new File("instance-reference.json"));
// Grant deploy access
@@ -1081,7 +1081,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/doesnotexist/application/doesnotexist/metering", GET)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
new File("instance1-metering.json"));
}
@@ -1099,7 +1099,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/" + applicationId.tenant().value() + "/cost", GET)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"months\":[\"2019-09\",\"2019-10\"]}");
CostInfo costInfo1 = new CostInfo(applicationId, ZoneId.from("prod", "us-south-1"),
@@ -1119,7 +1119,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/" + applicationId.tenant().value() + "/cost/2019-09", GET)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
new File("cost-report.json"));
}
@@ -1131,7 +1131,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
// PUT (update) non-existing tenant returns 403 as tenant access cannot be determined when the tenant does not exist
tester.assertResponse(request("/application/v4/tenant/tenant1", PUT)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT)
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT)
.data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"),
"{\n \"code\" : 403,\n \"message\" : \"Access denied\"\n}",
403);
@@ -1158,21 +1158,21 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/tenant1", POST)
.userIdentity(USER_ID)
.data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}")
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
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\"}")
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"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)
- .oktaAccessToken(OKTA_AT)
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT)
.data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"),
"{\"error-code\":\"BAD_REQUEST\",\"message\":\"Tenant 'tenant1' already exists\"}",
400);
@@ -1181,7 +1181,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/my_tenant_2", POST)
.userIdentity(USER_ID)
.data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}")
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"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);
@@ -1189,7 +1189,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/by-tenant2", POST)
.userIdentity(USER_ID)
.data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}")
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"error-code\":\"BAD_REQUEST\",\"message\":\"Athenz tenant name cannot have prefix 'by-'\"}",
400);
@@ -1197,19 +1197,19 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/hosted-vespa", POST)
.userIdentity(USER_ID)
.data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}")
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"error-code\":\"BAD_REQUEST\",\"message\":\"Tenant 'hosted-vespa' already exists\"}",
400);
// POST (create) an (empty) application
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", POST)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
new File("instance-reference.json"));
// Create the same application again
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", POST)
- .oktaAccessToken(OKTA_AT)
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT)
.userIdentity(USER_ID),
"{\"error-code\":\"BAD_REQUEST\",\"message\":\"Could not create 'tenant1.application1.instance1': Instance already exists\"}",
400);
@@ -1263,37 +1263,37 @@ public class ApplicationApiTest extends ControllerContainerTest {
// DELETE tenant which has an application
tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"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/instance/instance1", DELETE)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"message\":\"Deleted instance tenant1.application1.instance1\"}");
// DELETE application again - should produce 404
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", DELETE)
- .oktaAccessToken(OKTA_AT)
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT)
.userIdentity(USER_ID),
"{\"error-code\":\"NOT_FOUND\",\"message\":\"Could not delete instance 'tenant1.application1.instance1': Instance not found\"}",
404);
// GET cost of unknown tenant
- tester.assertResponse(request("/application/v4/tenant/no-such-tenant/cost", GET).userIdentity(USER_ID).oktaAccessToken(OKTA_AT),
+ tester.assertResponse(request("/application/v4/tenant/no-such-tenant/cost", GET).userIdentity(USER_ID).oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"error-code\":\"NOT_FOUND\",\"message\":\"Tenant 'no-such-tenant' does not exist\"}", 404);
- tester.assertResponse(request("/application/v4/tenant/no-such-tenant/cost/2018-01-01", GET).userIdentity(USER_ID).oktaAccessToken(OKTA_AT),
+ tester.assertResponse(request("/application/v4/tenant/no-such-tenant/cost/2018-01-01", GET).userIdentity(USER_ID).oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"error-code\":\"NOT_FOUND\",\"message\":\"Tenant 'no-such-tenant' does not exist\"}", 404);
// GET cost with invalid date string
- tester.assertResponse(request("/application/v4/tenant/tenant1/cost/not-a-valid-date", GET).userIdentity(USER_ID).oktaAccessToken(OKTA_AT),
+ tester.assertResponse(request("/application/v4/tenant/tenant1/cost/not-a-valid-date", GET).userIdentity(USER_ID).oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"error-code\":\"BAD_REQUEST\",\"message\":\"Could not parse year-month 'not-a-valid-date'\"}", 400);
// DELETE tenant
tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
new File("tenant-without-applications.json"));
// DELETE tenant again returns 403 as tenant access cannot be determined when the tenant does not exist
tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE)
@@ -1308,7 +1308,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/my-tenant", POST)
.userIdentity(USER_ID)
.data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}")
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"error-code\":\"BAD_REQUEST\",\"message\":\"Tenant 'my-tenant' already exists\"}",
400);
}
@@ -1336,7 +1336,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
// Creating a tenant for an Athens domain the user is not admin for is disallowed
tester.assertResponse(request("/application/v4/tenant/tenant1", POST)
.data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}")
- .oktaAccessToken(OKTA_AT)
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT)
.userIdentity(unauthorizedUser),
"{\"error-code\":\"FORBIDDEN\",\"message\":\"The user 'user.othertenant' is not admin in Athenz domain 'domain1'\"}",
403);
@@ -1345,21 +1345,21 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/tenant1", POST)
.data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}")
.userIdentity(authorizedUser)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
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/instance/instance1", POST)
.userIdentity(unauthorizedUser)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\n \"code\" : 403,\n \"message\" : \"Access denied\"\n}",
403);
// (Create it with the right tenant id)
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", POST)
.userIdentity(authorizedUser)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
new File("instance-reference.json"),
200);
@@ -1380,28 +1380,28 @@ public class ApplicationApiTest extends ControllerContainerTest {
// Create another instance under the application
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/default", POST)
.userIdentity(authorizedUser)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
new File("instance-reference-default.json"),
200);
// Deleting the application when more than one instance is present is forbidden
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", DELETE)
.userIdentity(authorizedUser)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"error-code\":\"BAD_REQUEST\",\"message\":\"Could not delete application; more than one instance present: [tenant1.application1, tenant1.application1.instance1]\"}",
400);
// Deleting one instance is OK
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/default", DELETE)
.userIdentity(authorizedUser)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"message\":\"Deleted instance tenant1.application1.default\"}",
200);
// (Deleting the application with the right tenant id)
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", DELETE)
.userIdentity(authorizedUser)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"message\":\"Deleted application tenant1.application1\"}",
200);
@@ -1417,7 +1417,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/tenant1", PUT)
.data("{\"athensDomain\":\"domain2\", \"property\":\"property1\"}")
.userIdentity(authorizedUser)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
"{\"tenant\":\"tenant1\",\"type\":\"ATHENS\",\"athensDomain\":\"domain2\",\"property\":\"property1\",\"applications\":[]}",
200);
@@ -1851,11 +1851,11 @@ public class ApplicationApiTest extends ControllerContainerTest {
tester.assertResponse(request("/application/v4/tenant/tenant1", POST)
.userIdentity(USER_ID)
.data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}")
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
new File("tenant-without-applications.json"));
tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", POST)
.userIdentity(USER_ID)
- .oktaAccessToken(OKTA_AT),
+ .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT),
new File("instance-reference.json"));
addScrewdriverUserToDeployRole(SCREWDRIVER_ID, ATHENZ_TENANT_DOMAIN,
new com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId("application1"));
@@ -1976,6 +1976,7 @@ public class ApplicationApiTest extends ControllerContainerTest {
private final Request.Method method;
private byte[] data = new byte[0];
private AthenzIdentity identity;
+ private OktaIdentityToken oktaIdentityToken;
private OktaAccessToken oktaAccessToken;
private String contentType = "application/json";
private Map<String, List<String>> headers = new HashMap<>();
@@ -1994,6 +1995,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 oktaIdentityToken(OktaIdentityToken oktaIdentityToken) { this.oktaIdentityToken = oktaIdentityToken; 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; }
@@ -2014,6 +2016,9 @@ public class ApplicationApiTest extends ControllerContainerTest {
if (identity != null) {
addIdentityToRequest(request, identity);
}
+ if (oktaIdentityToken != null) {
+ addOktaIdentityToken(request, oktaIdentityToken);
+ }
if (oktaAccessToken != null) {
addOktaAccessToken(request, oktaAccessToken);
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/OktaIdentityToken.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/OktaIdentityToken.java
new file mode 100644
index 00000000000..45f3024bec6
--- /dev/null
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/OktaIdentityToken.java
@@ -0,0 +1,40 @@
+// Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.athenz.api;
+
+import java.util.Objects;
+
+/**
+ * @author bjorncs
+ */
+public class OktaIdentityToken {
+
+ private final String token;
+
+ public OktaIdentityToken(String token) {
+ this.token = token;
+ }
+
+ public String token() {
+ return token;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ OktaIdentityToken that = (OktaIdentityToken) o;
+ return Objects.equals(token, that.token);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(token);
+ }
+
+ @Override
+ public String toString() {
+ return "OktaIdentityToken{" +
+ "token='" + token + '\'' +
+ '}';
+ }
+}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/DefaultZmsClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/DefaultZmsClient.java
index 7b5427216a1..8129763a6d6 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/DefaultZmsClient.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/DefaultZmsClient.java
@@ -6,6 +6,7 @@ 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.OktaAccessToken;
+import com.yahoo.vespa.athenz.api.OktaIdentityToken;
import com.yahoo.vespa.athenz.client.common.ClientBase;
import com.yahoo.vespa.athenz.client.zms.bindings.AccessResponseEntity;
import com.yahoo.vespa.athenz.client.zms.bindings.DomainListResponseEntity;
@@ -54,43 +55,45 @@ public class DefaultZmsClient extends ClientBase implements ZmsClient {
}
@Override
- public void createTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, OktaAccessToken token) {
+ public void createTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, OktaIdentityToken identityToken, OktaAccessToken accessToken) {
URI uri = zmsUrl.resolve(String.format("domain/%s/tenancy/%s", tenantDomain.getName(), providerService.getFullName()));
HttpUriRequest request = RequestBuilder.put()
.setUri(uri)
- .addHeader(creatOktaAccessTokenHeader(token))
+ .addHeader(createCookieHeaderWithOktaTokens(identityToken, accessToken))
.setEntity(toJsonStringEntity(new TenancyRequestEntity(tenantDomain, providerService, Collections.emptyList())))
.build();
execute(request, response -> readEntity(response, Void.class));
}
@Override
- public void deleteTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, OktaAccessToken token) {
+ public void deleteTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, OktaIdentityToken identityToken, OktaAccessToken accessToken) {
URI uri = zmsUrl.resolve(String.format("domain/%s/tenancy/%s", tenantDomain.getName(), providerService.getFullName()));
HttpUriRequest request = RequestBuilder.delete()
.setUri(uri)
- .addHeader(creatOktaAccessTokenHeader(token))
+ .addHeader(createCookieHeaderWithOktaTokens(identityToken, accessToken))
.build();
execute(request, response -> readEntity(response, Void.class));
}
@Override
- public void createProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup, Set<RoleAction> roleActions, OktaAccessToken token) {
+ public void createProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup,
+ Set<RoleAction> roleActions, OktaIdentityToken identityToken, OktaAccessToken accessToken) {
URI uri = zmsUrl.resolve(String.format("domain/%s/provDomain/%s/provService/%s/resourceGroup/%s", tenantDomain.getName(), providerService.getDomainName(), providerService.getName(), resourceGroup));
HttpUriRequest request = RequestBuilder.put()
.setUri(uri)
- .addHeader(creatOktaAccessTokenHeader(token))
+ .addHeader(createCookieHeaderWithOktaTokens(identityToken, accessToken))
.setEntity(toJsonStringEntity(new ProviderResourceGroupRolesRequestEntity(providerService, tenantDomain, roleActions, resourceGroup)))
.build();
execute(request, response -> readEntity(response, Void.class)); // Note: The ZMS API will actually return a json object that is similar to ProviderResourceGroupRolesRequestEntity
}
@Override
- public void deleteProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup, OktaAccessToken token) {
+ public void deleteProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup,
+ OktaIdentityToken identityToken, OktaAccessToken accessToken) {
URI uri = zmsUrl.resolve(String.format("domain/%s/provDomain/%s/provService/%s/resourceGroup/%s", tenantDomain.getName(), providerService.getDomainName(), providerService.getName(), resourceGroup));
HttpUriRequest request = RequestBuilder.delete()
.setUri(uri)
- .addHeader(creatOktaAccessTokenHeader(token))
+ .addHeader(createCookieHeaderWithOktaTokens(identityToken, accessToken))
.build();
execute(request, response -> readEntity(response, Void.class));
}
@@ -132,7 +135,8 @@ public class DefaultZmsClient extends ClientBase implements ZmsClient {
});
}
- private static Header creatOktaAccessTokenHeader(OktaAccessToken token) {
- return new BasicHeader("Cookie", String.format("okta_at=%s", token.token()));
+ private static Header createCookieHeaderWithOktaTokens(OktaIdentityToken identityToken, OktaAccessToken accessToken) {
+ return new BasicHeader("Cookie", String.format("okta_at=%s; okta_it=%s", accessToken.token(), identityToken.token()));
}
+
}
diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/ZmsClient.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/ZmsClient.java
index e78478bc1a2..6a11a69a797 100644
--- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/ZmsClient.java
+++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/client/zms/ZmsClient.java
@@ -5,10 +5,9 @@ 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.api.OktaIdentityToken;
-import java.time.Instant;
import java.util.List;
import java.util.Set;
@@ -17,13 +16,17 @@ import java.util.Set;
*/
public interface ZmsClient extends AutoCloseable {
- void createTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, OktaAccessToken token);
+ void createTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService,
+ OktaIdentityToken identityToken, OktaAccessToken accessToken);
- void deleteTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, OktaAccessToken token);
+ void deleteTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService,
+ OktaIdentityToken identityToken, OktaAccessToken accessToken);
- void createProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup, Set<RoleAction> roleActions, OktaAccessToken token);
+ void createProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup,
+ Set<RoleAction> roleActions, OktaIdentityToken identityToken, OktaAccessToken accessToken);
- void deleteProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup, OktaAccessToken token);
+ void deleteProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup,
+ OktaIdentityToken identityToken, OktaAccessToken accessToken);
boolean getMembership(AthenzRole role, AthenzIdentity identity);