diff options
12 files changed, 154 insertions, 235 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 63212c9c200..121abc8c9e3 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 @@ -9,9 +9,7 @@ import com.yahoo.vespa.athenz.api.AthenzPolicy; 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.AthenzUser; -import com.yahoo.vespa.athenz.api.OktaAccessToken; -import com.yahoo.vespa.athenz.api.OktaIdentityToken; +import com.yahoo.vespa.athenz.api.OAuthCredentials; import com.yahoo.vespa.athenz.client.zms.RoleAction; import com.yahoo.vespa.athenz.client.zms.ZmsClient; import com.yahoo.vespa.athenz.client.zms.ZmsClientException; @@ -48,15 +46,13 @@ public class ZmsClientMock implements ZmsClient { } @Override - public void createTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, - OktaIdentityToken identityToken, OktaAccessToken accessToken) { + public void createTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, OAuthCredentials oAuthCredentials) { log("createTenancy(tenantDomain='%s')", tenantDomain); getDomainOrThrow(tenantDomain, false).isVespaTenant = true; } @Override - public void deleteTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, - OktaIdentityToken identityToken, OktaAccessToken accessToken) { + public void deleteTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, OAuthCredentials oAuthCredentials) { log("deleteTenancy(tenantDomain='%s')", tenantDomain); AthenzDbMock.Domain domain = getDomainOrThrow(tenantDomain, false); domain.isVespaTenant = false; @@ -66,7 +62,7 @@ public class ZmsClientMock implements ZmsClient { @Override public void createProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup, - Set<RoleAction> roleActions, OktaIdentityToken identityToken, OktaAccessToken accessToken) { + Set<RoleAction> roleActions, OAuthCredentials oAuthCredentials) { log("createProviderResourceGroup(tenantDomain='%s', resourceGroup='%s')", tenantDomain, resourceGroup); AthenzDbMock.Domain domain = getDomainOrThrow(tenantDomain, true); ApplicationId applicationId = new ApplicationId(resourceGroup); @@ -77,7 +73,7 @@ public class ZmsClientMock implements ZmsClient { @Override public void deleteProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup, - OktaIdentityToken identityToken, OktaAccessToken accessToken) { + OAuthCredentials oAuthCredentials) { 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 a0b70eb88ab..eceb098d702 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 @@ -16,8 +16,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.AthenzUser; -import com.yahoo.vespa.athenz.api.OktaAccessToken; -import com.yahoo.vespa.athenz.api.OktaIdentityToken; +import com.yahoo.vespa.athenz.api.OAuthCredentials; import com.yahoo.vespa.athenz.client.zms.RoleAction; import com.yahoo.vespa.athenz.client.zms.ZmsClient; import com.yahoo.vespa.athenz.client.zms.ZmsClientException; @@ -109,7 +108,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.identityToken(), athenzCredentials.accessToken()); + zmsClient.createTenancy(domain, service, athenzCredentials.oAuthCredentials()); } return tenant; @@ -150,14 +149,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.identityToken(), athenzCredentials.accessToken()); + zmsClient.createTenancy(newDomain, service, athenzCredentials.oAuthCredentials()); for (Application application : applications) - createApplication(newDomain, application.id().application(), athenzCredentials.identityToken(), athenzCredentials.accessToken()); + createApplication(newDomain, application.id().application(), athenzCredentials.oAuthCredentials()); log("deleteTenancy(tenantDomain=%s, service=%s)", oldDomain, service); for (Application application : applications) - deleteApplication(oldDomain, application.id().application(), athenzCredentials.identityToken(), athenzCredentials.accessToken()); - zmsClient.deleteTenancy(oldDomain, service, athenzCredentials.identityToken(), athenzCredentials.accessToken()); + deleteApplication(oldDomain, application.id().application(), athenzCredentials.oAuthCredentials()); + zmsClient.deleteTenancy(oldDomain, service, athenzCredentials.oAuthCredentials()); } return tenant; @@ -169,7 +168,7 @@ public class AthenzFacade implements AccessControl { AthenzDomain tenantDomain = athenzCredentials.domain(); log("deleteTenancy(tenantDomain=%s, service=%s)", tenantDomain, service); try { - zmsClient.deleteTenancy(tenantDomain, service, athenzCredentials.identityToken(), athenzCredentials.accessToken()); + zmsClient.deleteTenancy(tenantDomain, service, athenzCredentials.oAuthCredentials()); } catch (ZmsClientException e) { if (e.getErrorCode() == 404) { log.log(Level.WARNING, @@ -185,16 +184,16 @@ public class AthenzFacade implements AccessControl { @Override public void createApplication(TenantAndApplicationId id, Credentials credentials) { AthenzCredentials athenzCredentials = (AthenzCredentials) credentials; - createApplication(athenzCredentials.domain(), id.application(), athenzCredentials.identityToken(), athenzCredentials.accessToken()); + createApplication(athenzCredentials.domain(), id.application(), athenzCredentials.oAuthCredentials()); } - private void createApplication(AthenzDomain domain, ApplicationName application, OktaIdentityToken identityToken, OktaAccessToken accessToken) { + private void createApplication(AthenzDomain domain, ApplicationName application, OAuthCredentials oAuthCredentials) { 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, identityToken, accessToken); + zmsClient.createProviderResourceGroup(domain, service, application.value(), tenantRoleActions, oAuthCredentials); } catch (ZmsClientException e) { if (e.getErrorCode() == com.yahoo.jdisc.Response.Status.FORBIDDEN) @@ -211,7 +210,7 @@ public class AthenzFacade implements AccessControl { athenzCredentials.domain(), service.getDomain().getName(), service.getName(), id.application()); try { zmsClient.deleteProviderResourceGroup(athenzCredentials.domain(), service, id.application().value(), - athenzCredentials.identityToken(), athenzCredentials.accessToken()); + athenzCredentials.oAuthCredentials()); } catch (ZmsClientException e) { if (e.getErrorCode() == 404) { log.log(Level.WARNING, @@ -243,10 +242,10 @@ public class AthenzFacade implements AccessControl { zmsClient.addRoleMember(new AthenzRole(tenantDomain, "tenancy." + service.getFullName() + ".admin"), user, Optional.empty()); } - private void deleteApplication(AthenzDomain domain, ApplicationName application, OktaIdentityToken identityToken, OktaAccessToken accessToken) { + private void deleteApplication(AthenzDomain domain, ApplicationName application, OAuthCredentials oAuthCredentials) { log("deleteProviderResourceGroup(tenantDomain=%s, providerDomain=%s, service=%s, resourceGroup=%s)", domain, service.getDomain().getName(), service.getName(), application); - zmsClient.deleteProviderResourceGroup(domain, service, application.value(), identityToken, accessToken); + zmsClient.deleteProviderResourceGroup(domain, service, application.value(), oAuthCredentials); } 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 876882b7f19..f4f6df28ebc 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 @@ -8,8 +8,7 @@ import com.yahoo.slime.Inspector; import com.yahoo.text.Text; 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.athenz.api.OAuthCredentials; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.TenantController; import com.yahoo.vespa.hosted.controller.api.identifiers.Property; @@ -19,7 +18,6 @@ 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. @@ -48,22 +46,7 @@ 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 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)); + OAuthCredentials.fromOktaRequestContext(request.context())); } 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 04b0d6adafd..fd74626a6cf 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 com.yahoo.vespa.athenz.api.OktaIdentityToken; +import com.yahoo.vespa.athenz.api.OAuthCredentials; import static java.util.Objects.requireNonNull; @@ -18,15 +17,12 @@ import static java.util.Objects.requireNonNull; public class AthenzCredentials extends Credentials { private final AthenzDomain domain; - private final OktaIdentityToken identityToken; - private final OktaAccessToken accessToken; + private final OAuthCredentials oAuthCredentials; - public AthenzCredentials(AthenzPrincipal user, AthenzDomain domain, - OktaIdentityToken identityToken, OktaAccessToken accessToken) { + public AthenzCredentials(AthenzPrincipal user, AthenzDomain domain, OAuthCredentials oAuthCredentials) { super(user); this.domain = requireNonNull(domain); - this.accessToken = requireNonNull(accessToken); - this.identityToken = requireNonNull(identityToken); + this.oAuthCredentials = requireNonNull(oAuthCredentials); } @Override @@ -35,11 +31,7 @@ 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 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; } - + /** Returns the OAuth credentials required for Athenz tenancy operation */ + public OAuthCredentials oAuthCredentials() { return oAuthCredentials; } } 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 99c97b3bdd6..5cf55930274 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 @@ -16,8 +16,7 @@ import com.yahoo.text.Text; 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.athenz.api.OAuthCredentials; import com.yahoo.vespa.flags.InMemoryFlagSource; import com.yahoo.vespa.flags.PermanentFlags; import com.yahoo.vespa.hosted.controller.api.identifiers.Property; @@ -320,7 +319,7 @@ public final class ControllerTester { new Property("Property" + propertyId), Optional.ofNullable(propertyId).map(Object::toString).map(PropertyId::new)); AthenzCredentials credentials = new AthenzCredentials( - new AthenzPrincipal(user), domain, new OktaIdentityToken("okta-identity-token"), new OktaAccessToken("okta-access-token")); + new AthenzPrincipal(user), domain, OAuthCredentials.createForTesting("okta-access-token", "okta-identity-token")); controller().tenants().create(tenantSpec, credentials); contact.ifPresent(value -> controller().tenants().lockOrThrow(name, LockedTenant.Athenz.class, tenant -> controller().tenants().store(tenant.with(value)))); @@ -342,8 +341,7 @@ public final class ControllerTester { case athenz: return new AthenzCredentials(new AthenzPrincipal(new AthenzUser("user")), ((AthenzTenant) tenant).domain(), - new OktaIdentityToken("okta-identity-token"), - new OktaAccessToken("okta-access-token")); + OAuthCredentials.createForTesting("okta-access-token", "okta-identity-token")); case cloud: return new Credentials(new SimplePrincipal("dev")); 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 89394e8a30c..621a3272918 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/ControllerContainerTest.java @@ -7,8 +7,7 @@ import com.yahoo.application.container.handler.Request; 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.athenz.api.OAuthCredentials; import com.yahoo.vespa.hosted.controller.api.integration.athenz.AthenzClientFactoryMock; import org.junit.After; import org.junit.Before; @@ -163,13 +162,9 @@ 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()); + protected static Request addOAuthCredentials(Request request, OAuthCredentials oAuthCredentials) { + request.getHeaders().put(OKTA_IDENTITY_TOKEN_HEADER_NAME, oAuthCredentials.idToken()); + request.getHeaders().put(OKTA_ACCESS_TOKEN_HEADER_NAME, oAuthCredentials.accessToken()); 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 61e9ea02acc..32999d24a03 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 @@ -24,8 +24,7 @@ import com.yahoo.vespa.athenz.api.AthenzDomain; import com.yahoo.vespa.athenz.api.AthenzIdentity; import com.yahoo.vespa.athenz.api.AthenzPrincipal; import com.yahoo.vespa.athenz.api.AthenzUser; -import com.yahoo.vespa.athenz.api.OktaAccessToken; -import com.yahoo.vespa.athenz.api.OktaIdentityToken; +import com.yahoo.vespa.athenz.api.OAuthCredentials; import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.ControllerTester; import com.yahoo.vespa.hosted.controller.Instance; @@ -150,8 +149,7 @@ 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 OktaIdentityToken OKTA_IT = new OktaIdentityToken("okta-it"); - private static final OktaAccessToken OKTA_AT = new OktaAccessToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.he0ErCNloe4J7Id0Ry2SEDg09lKkZkfsRiGsdX_vgEg"); + private static final OAuthCredentials OKTA_CREDENTIALS = OAuthCredentials.createForTesting("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.he0ErCNloe4J7Id0Ry2SEDg09lKkZkfsRiGsdX_vgEg", "okta-it"); private ContainerTester tester; @@ -175,12 +173,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).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), new File("tenant-without-applications.json")); // PUT (modify) a tenant tester.assertResponse(request("/application/v4/tenant/tenant1", PUT) .userIdentity(USER_ID) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT) + .oAuthCredentials(OKTA_CREDENTIALS) .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), new File("tenant-without-applications.json")); @@ -192,13 +190,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).oktaIdentityToken(OKTA_IT) + .oAuthCredentials(OKTA_CREDENTIALS) .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).oktaIdentityToken(OKTA_IT) + .oAuthCredentials(OKTA_CREDENTIALS) .data("{\"athensDomain\":\"domain2\", \"property\":\"property2\", \"propertyId\":\"1234\"}"), new File("tenant-without-applications-with-id.json")); // GET a tenant with property ID and contact information @@ -211,7 +209,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).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), new File("instance-reference.json")); // GET a tenant tester.assertResponse(request("/application/v4/tenant/tenant1", GET).userIdentity(USER_ID), @@ -322,7 +320,7 @@ public class ApplicationApiTest extends ControllerContainerTest { // DELETE a user instance tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/myuser", DELETE) .userIdentity(USER_ID) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"message\":\"Deleted instance tenant1.application1.myuser\"}"); addScrewdriverUserToDeployRole(SCREWDRIVER_ID, @@ -349,7 +347,7 @@ public class ApplicationApiTest extends ControllerContainerTest { // POST (create) another application tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/instance/default", POST) .userIdentity(USER_ID) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), new File("instance-reference-2.json")); ApplicationId id2 = ApplicationId.from("tenant2", "application2", "instance1"); @@ -436,13 +434,13 @@ public class ApplicationApiTest extends ControllerContainerTest { // DELETE instance 1 of 2 tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2/instance/default", DELETE) .userIdentity(USER_ID) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"message\":\"Deleted instance tenant2.application2.default\"}"); // DELETE application with only one instance left tester.assertResponse(request("/application/v4/tenant/tenant2/application/application2", DELETE) .userIdentity(USER_ID) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"message\":\"Deleted application tenant2.application2\"}"); // Set version 6.1 to broken to change compile version for. @@ -707,7 +705,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).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), new File("delete-with-active-deployments.json"), 400); // DELETE (deactivate) a deployment - dev @@ -874,17 +872,17 @@ public class ApplicationApiTest extends ControllerContainerTest { // DELETE the application which no longer has any deployments tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", DELETE) .userIdentity(USER_ID) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"message\":\"Deleted application tenant1.application1\"}"); // DELETE an empty tenant tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE).userIdentity(USER_ID) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"message\":\"Deleted tenant tenant1\"}"); // The tenant is not found tester.assertResponse(request("/application/v4/tenant/tenant1", GET).userIdentity(USER_ID) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"error-code\":\"NOT_FOUND\",\"message\":\"Tenant 'tenant1' does not exist\"}", 404); // ... unless we specify to show deleted tenants @@ -895,14 +893,14 @@ public class ApplicationApiTest extends ControllerContainerTest { // Tenant cannot be recreated tester.assertResponse(request("/application/v4/tenant/tenant1", POST).userIdentity(USER_ID) .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Tenant 'tenant1' already exists\"}", 400); // Forget a deleted tenant tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE).properties(Map.of("forget", "true")) .data("{\"athensDomain\":\"domain1\"}") - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT) + .oAuthCredentials(OKTA_CREDENTIALS) .userIdentity(HOSTED_VESPA_OPERATOR), "{\"message\":\"Deleted tenant tenant1\"}"); tester.assertResponse(request("/application/v4/tenant/tenant1", GET).properties(Map.of("includeDeleted", "true")) @@ -1072,7 +1070,7 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/doesnotexist/application/doesnotexist/metering", GET) .userIdentity(USER_ID) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), new File("instance1-metering.json")); } @@ -1100,7 +1098,7 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/deployment", DELETE) .userIdentity(USER_ID) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"message\":\"All deployments removed\"}"); assertEquals(Set.of(ZoneId.from("dev.us-east-1")), app.instance().deployments().keySet()); @@ -1113,7 +1111,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).oktaIdentityToken(OKTA_IT) + .oAuthCredentials(OKTA_CREDENTIALS) .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), accessDenied, 403); @@ -1146,21 +1144,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).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), 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).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"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).oktaIdentityToken(OKTA_IT) + .oAuthCredentials(OKTA_CREDENTIALS) .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}"), "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Tenant 'tenant1' already exists\"}", 400); @@ -1169,7 +1167,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).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"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); @@ -1177,19 +1175,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).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"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).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), 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).oktaIdentityToken(OKTA_IT) + .oAuthCredentials(OKTA_CREDENTIALS) .userIdentity(USER_ID), "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Could not create 'tenant1.application1.instance1': Instance already exists\"}", 400); @@ -1226,18 +1224,18 @@ 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).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"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).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"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).oktaIdentityToken(OKTA_IT) + .oAuthCredentials(OKTA_CREDENTIALS) .userIdentity(USER_ID), "{\"error-code\":\"NOT_FOUND\",\"message\":\"Could not delete instance 'tenant1.application1.instance1': Instance not found\"}", 404); @@ -1245,14 +1243,14 @@ public class ApplicationApiTest extends ControllerContainerTest { // DELETE and forget an application as non-operator tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE).properties(Map.of("forget", "true")) .userIdentity(USER_ID) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"error-code\":\"FORBIDDEN\",\"message\":\"Only operators can forget a tenant\"}", 403); // DELETE tenant tester.assertResponse(request("/application/v4/tenant/tenant1", DELETE) .userIdentity(USER_ID) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"message\":\"Deleted tenant tenant1\"}"); // 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) @@ -1268,7 +1266,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).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"error-code\":\"BAD_REQUEST\",\"message\":\"Tenant 'my-tenant' already exists\"}", 400); } @@ -1296,7 +1294,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).oktaIdentityToken(OKTA_IT) + .oAuthCredentials(OKTA_CREDENTIALS) .userIdentity(unauthorizedUser), "{\"error-code\":\"FORBIDDEN\",\"message\":\"The user 'user.othertenant' is not admin in Athenz domain 'domain1'\"}", 403); @@ -1305,21 +1303,21 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant1", POST) .data("{\"athensDomain\":\"domain1\", \"property\":\"property1\"}") .userIdentity(authorizedUser) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), 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).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), accessDenied, 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).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), new File("instance-reference.json"), 200); @@ -1340,14 +1338,14 @@ 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).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), new File("instance-reference-default.json"), 200); // (Deleting the application with the right tenant id) tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1", DELETE) .userIdentity(authorizedUser) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"message\":\"Deleted application tenant1.application1\"}", 200); @@ -1363,7 +1361,7 @@ public class ApplicationApiTest extends ControllerContainerTest { tester.assertResponse(request("/application/v4/tenant/tenant1", PUT) .data("{\"athensDomain\":\"domain2\", \"property\":\"property1\"}") .userIdentity(authorizedUser) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), new File("tenant1.json"), 200); @@ -1468,7 +1466,7 @@ public class ApplicationApiTest extends ControllerContainerTest { new Property("vespa"), Optional.empty()); AthenzCredentials credentials = new AthenzCredentials( - new AthenzPrincipal(new AthenzUser(developer.id())), sandboxDomain, OKTA_IT, OKTA_AT); + new AthenzPrincipal(new AthenzUser(developer.id())), sandboxDomain, OKTA_CREDENTIALS); tester.controller().tenants().create(tenantSpec, credentials); tester.controller().applications().createApplication(TenantAndApplicationId.from("sandbox", "myapp"), credentials); @@ -1634,21 +1632,21 @@ public class ApplicationApiTest extends ControllerContainerTest { // Not allowed to request apis not listed in feature flag allowed-service-view-apis. e.g /document/v1 tester.assertResponse(request(serviceApi + "/storagenode-awe3slno6mmq2fye191y324jl/document/v1/", GET) .userIdentity(USER_ID) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"error-code\":\"FORBIDDEN\",\"message\":\"Access denied\"}", 403); // Test path traversal tester.assertResponse(request(serviceApi + "/storagenode-awe3slno6mmq2fye191y324jl/state/v1/../../document/v1/", GET) .userIdentity(USER_ID) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"error-code\":\"FORBIDDEN\",\"message\":\"Access denied\"}", 403); // Test urlencoded path traversal tester.assertResponse(request(serviceApi + "/storagenode-awe3slno6mmq2fye191y324jl/state%2Fv1%2F..%2F..%2Fdocument%2Fv1%2F", GET) .userIdentity(USER_ID) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), "{\"error-code\":\"FORBIDDEN\",\"message\":\"Access denied\"}", 403); } @@ -1662,7 +1660,7 @@ 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).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), new File("tenant-without-applications.json")); // Deploy application @@ -1675,8 +1673,8 @@ public class ApplicationApiTest extends ControllerContainerTest { // POST (deploy) an application to start a manual deployment to dev tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1/deploy/dev-us-east-1/", POST) .data(entity) - .oktaIdentityToken(OKTA_IT) - .oktaAccessToken(OKTA_AT) + .oAuthCredentials(OKTA_CREDENTIALS) + .userIdentity(USER_ID), "{\"message\":\"Deployment started in run 1 of dev-us-east-1 for tenant1.application1.instance1. This may take about 15 minutes the first time.\",\"run\":1}"); @@ -1772,11 +1770,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).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), new File("tenant-without-applications.json")); tester.assertResponse(request("/application/v4/tenant/tenant1/application/application1/instance/instance1", POST) .userIdentity(USER_ID) - .oktaAccessToken(OKTA_AT).oktaIdentityToken(OKTA_IT), + .oAuthCredentials(OKTA_CREDENTIALS), new File("instance-reference.json")); addScrewdriverUserToDeployRole(SCREWDRIVER_ID, ATHENZ_TENANT_DOMAIN, ApplicationName.from("application1")); @@ -1874,8 +1872,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 OAuthCredentials oAuthCredentials; private String contentType = "application/json"; private final Map<String, List<String>> headers = new HashMap<>(); private final Map<String, String> properties = new HashMap<>(); @@ -1893,8 +1890,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 oAuthCredentials(OAuthCredentials oAuthCredentials) { this.oAuthCredentials = oAuthCredentials; return this; } private RequestBuilder contentType(String contentType) { this.contentType = contentType; return this; } private RequestBuilder recursive(String recursive) {return properties(Map.of("recursive", recursive)); } private RequestBuilder properties(Map<String, String> properties) { this.properties.putAll(properties); return this; } @@ -1914,15 +1910,8 @@ public class ApplicationApiTest extends ControllerContainerTest { request.getHeaders().addAll(headers); request.getHeaders().put("Content-Type", contentType); // user and domain parameters are translated to a Principal by MockAuthorizer as we do not run HTTP filters - if (identity != null) { - addIdentityToRequest(request, identity); - } - if (oktaIdentityToken != null) { - addOktaIdentityToken(request, oktaIdentityToken); - } - if (oktaAccessToken != null) { - addOktaAccessToken(request, oktaAccessToken); - } + if (identity != null) addIdentityToRequest(request, identity); + if (oAuthCredentials != null) addOAuthCredentials(request, oAuthCredentials); return request; } } diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/OAuthCredentials.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/OAuthCredentials.java new file mode 100644 index 00000000000..1798a679b27 --- /dev/null +++ b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/OAuthCredentials.java @@ -0,0 +1,52 @@ +// Copyright Yahoo. 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.Map; +import java.util.Objects; +import java.util.Optional; + +/** + * @author freva + */ +public class OAuthCredentials { + + private final String accessTokenCookieName; + private final String accessToken; + private final String idTokenCookieName; + private final String idToken; + + private OAuthCredentials(String accessTokenCookieName, String accessToken, String idTokenCookieName, String idToken) { + this.accessTokenCookieName = Objects.requireNonNull(accessTokenCookieName); + this.accessToken = Objects.requireNonNull(accessToken); + this.idTokenCookieName = Objects.requireNonNull(idTokenCookieName); + this.idToken = Objects.requireNonNull(idToken); + } + + public String accessToken() { return accessToken; } + public String idToken() { return idToken; } + + public String asCookie() { + return String.format("%s=%s; %s=%s", accessTokenCookieName, accessToken, idTokenCookieName, idToken); + } + + public static OAuthCredentials fromOktaRequestContext(Map<String, Object> requestContext) { + return new OAuthCredentials("okta_at", requireToken(requestContext, "okta.access-token", "No Okta Access Token provided"), + "okta_it", requireToken(requestContext, "okta.identity-token", "No Okta Identity Token provided")); + } + + public static OAuthCredentials fromAuth0RequestContext(Map<String, Object> requestContext) { + return new OAuthCredentials("access_token", requireToken(requestContext, "auth0.access-token", "No Auth0 Access Token provided"), + "id_token", requireToken(requestContext, "auth0.identity-token", "No Auth0 Identity Token provided")); + } + + public static OAuthCredentials createForTesting(String accessToken, String idToken) { + return new OAuthCredentials("accessToken", accessToken, "idToken", idToken); + } + + private static String requireToken(Map<String, Object> context, String attribute, String errorMessage) { + return Optional.ofNullable(context.get(attribute)) + .map(String.class::cast) + .orElseThrow(() -> new IllegalArgumentException(errorMessage)); + } + +} diff --git a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/OktaAccessToken.java b/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/OktaAccessToken.java deleted file mode 100644 index 80f769f7fcd..00000000000 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/OktaAccessToken.java +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright Yahoo. 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 OktaAccessToken { - - private final String token; - - public OktaAccessToken(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; - OktaAccessToken that = (OktaAccessToken) o; - return Objects.equals(token, that.token); - } - - @Override - public int hashCode() { - return Objects.hash(token); - } - - @Override - public String toString() { - return "OktaAccessToken{" + - "token='" + token + '\'' + - '}'; - } -} 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 deleted file mode 100644 index dfe69c7d9d4..00000000000 --- a/vespa-athenz/src/main/java/com/yahoo/vespa/athenz/api/OktaIdentityToken.java +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright Yahoo. 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 d83eab9e339..3c60d5bbcc3 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 @@ -9,8 +9,7 @@ import com.yahoo.vespa.athenz.api.AthenzPolicy; 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.api.OAuthCredentials; import com.yahoo.vespa.athenz.client.ErrorHandler; import com.yahoo.vespa.athenz.client.common.ClientBase; import com.yahoo.vespa.athenz.client.zms.bindings.AccessResponseEntity; @@ -74,33 +73,33 @@ public class DefaultZmsClient extends ClientBase implements ZmsClient { } @Override - public void createTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, OktaIdentityToken identityToken, OktaAccessToken accessToken) { + public void createTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, OAuthCredentials oAuthCredentials) { URI uri = zmsUrl.resolve(String.format("domain/%s/tenancy/%s", tenantDomain.getName(), providerService.getFullName())); HttpUriRequest request = RequestBuilder.put() .setUri(uri) - .addHeader(createCookieHeaderWithOktaTokens(identityToken, accessToken)) + .addHeader(createCookieHeader(oAuthCredentials)) .setEntity(toJsonStringEntity(new TenancyRequestEntity(tenantDomain, providerService, Collections.emptyList()))) .build(); execute(request, response -> readEntity(response, Void.class)); } @Override - public void deleteTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, OktaIdentityToken identityToken, OktaAccessToken accessToken) { + public void deleteTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, OAuthCredentials oAuthCredentials) { URI uri = zmsUrl.resolve(String.format("domain/%s/tenancy/%s", tenantDomain.getName(), providerService.getFullName())); HttpUriRequest request = RequestBuilder.delete() .setUri(uri) - .addHeader(createCookieHeaderWithOktaTokens(identityToken, accessToken)) + .addHeader(createCookieHeader(oAuthCredentials)) .build(); execute(request, response -> readEntity(response, Void.class)); } @Override public void createProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup, - Set<RoleAction> roleActions, OktaIdentityToken identityToken, OktaAccessToken accessToken) { + Set<RoleAction> roleActions, OAuthCredentials oAuthCredentials) { 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(createCookieHeaderWithOktaTokens(identityToken, accessToken)) + .addHeader(createCookieHeader(oAuthCredentials)) .setEntity(toJsonStringEntity(new ResourceGroupRolesEntity(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 @@ -108,11 +107,11 @@ public class DefaultZmsClient extends ClientBase implements ZmsClient { @Override public void deleteProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup, - OktaIdentityToken identityToken, OktaAccessToken accessToken) { + OAuthCredentials oAuthCredentials) { 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(createCookieHeaderWithOktaTokens(identityToken, accessToken)) + .addHeader(createCookieHeader(oAuthCredentials)) .build(); execute(request, response -> readEntity(response, Void.class)); } @@ -404,8 +403,8 @@ public class DefaultZmsClient extends ClientBase implements ZmsClient { execute(request, response -> readEntity(response, Void.class)); } - private static Header createCookieHeaderWithOktaTokens(OktaIdentityToken identityToken, OktaAccessToken accessToken) { - return new BasicHeader("Cookie", String.format("okta_at=%s; okta_it=%s", accessToken.token(), identityToken.token())); + private static Header createCookieHeader(OAuthCredentials oAuthCredentials) { + return new BasicHeader("Cookie", oAuthCredentials.asCookie()); } } 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 38d11d33d74..bd73913ea64 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 @@ -8,9 +8,7 @@ import com.yahoo.vespa.athenz.api.AthenzPolicy; 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.AthenzUser; -import com.yahoo.vespa.athenz.api.OktaAccessToken; -import com.yahoo.vespa.athenz.api.OktaIdentityToken; +import com.yahoo.vespa.athenz.api.OAuthCredentials; import java.time.Instant; import java.util.List; @@ -23,17 +21,15 @@ import java.util.Set; */ public interface ZmsClient extends AutoCloseable { - void createTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, - OktaIdentityToken identityToken, OktaAccessToken accessToken); + void createTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, OAuthCredentials oAuthCredentials); - void deleteTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, - OktaIdentityToken identityToken, OktaAccessToken accessToken); + void deleteTenancy(AthenzDomain tenantDomain, AthenzIdentity providerService, OAuthCredentials oAuthCredentials); void createProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup, - Set<RoleAction> roleActions, OktaIdentityToken identityToken, OktaAccessToken accessToken); + Set<RoleAction> roleActions, OAuthCredentials oAuthCredentials); void deleteProviderResourceGroup(AthenzDomain tenantDomain, AthenzIdentity providerService, String resourceGroup, - OktaIdentityToken identityToken, OktaAccessToken accessToken); + OAuthCredentials oAuthCredentials); /** For manual tenancy provisioning - only creates roles/policies on provider domain */ void createTenantResourceGroup(AthenzDomain tenantDomain, AthenzIdentity provider, String resourceGroup, |