diff options
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); |