diff options
author | Morten Tokle <mortent@yahooinc.com> | 2023-06-12 22:45:43 +0200 |
---|---|---|
committer | Morten Tokle <mortent@yahooinc.com> | 2023-06-12 22:45:43 +0200 |
commit | 362ddb0749608a5ace2be1caa5507ca9d3895eaf (patch) | |
tree | eff7d386e26d0d293f123a16139d10b19e84ebd7 /controller-server/src/test | |
parent | 5f25e0ba346c04ccc27c60cc410c0ed2fdb6b06b (diff) |
API to generate/list/delete dataplane tokens
Diffstat (limited to 'controller-server/src/test')
2 files changed, 118 insertions, 0 deletions
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java index 6012b491fe7..841e46ad881 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiCloudTest.java @@ -28,6 +28,7 @@ import com.yahoo.vespa.hosted.controller.security.Auth0Credentials; import com.yahoo.vespa.hosted.controller.security.CloudTenantSpec; import com.yahoo.vespa.hosted.controller.security.Credentials; import com.yahoo.vespa.hosted.controller.tenant.CloudTenant; +import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -35,12 +36,14 @@ import java.io.File; import java.util.Collections; import java.util.Optional; import java.util.Set; +import java.util.regex.Pattern; import static com.yahoo.application.container.handler.Request.Method.DELETE; import static com.yahoo.application.container.handler.Request.Method.GET; import static com.yahoo.application.container.handler.Request.Method.POST; import static com.yahoo.application.container.handler.Request.Method.PUT; import static com.yahoo.vespa.hosted.controller.restapi.application.ApplicationApiTest.createApplicationSubmissionData; +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -462,6 +465,39 @@ public class ApplicationApiCloudTest extends ControllerContainerCloudTest { assertTrue(tester.controller().applications().getApplication(TenantAndApplicationId.from(tenantName, application)).isPresent()); } + @Test + void dataplane_token_test() { + tester.assertResponse(request("/application/v4/tenant/scoober/token", GET) + .roles(Role.developer(tenantName)), + "{\"tokens\":[]}", 200); + + String regexGenerateToken = "\\{\"id\":\"myTokenId\",\"token\":\"vespa_cloud_.*\",\"fingerprint\":\".*\"}"; + tester.assertResponse(request("/application/v4/tenant/scoober/token/myTokenId", POST).roles(Role.developer(tenantName)), + (response) -> Assertions.assertThat(new String(response.getBody(), UTF_8)).matches(Pattern.compile(regexGenerateToken)), + 200); + + String regexListTokens = "\\{\"tokens\":\\[\\{\"id\":\"myTokenId\",\"fingerprints\":\\[\\{\"value\":\".*\",\"created-at\":\".*\",\"author\":\"user@test\"}]}]}"; + tester.assertResponse(request("/application/v4/tenant/scoober/token", GET) + .roles(Role.developer(tenantName)), + (response) -> Assertions.assertThat(new String(response.getBody(), UTF_8)).matches(Pattern.compile(regexListTokens)), + 200); + + // Rejects invalid tokenIds on create + tester.assertResponse(request("/application/v4/tenant/scoober/token/foo+bar", POST).roles(Role.developer(tenantName)), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"tokenId must match '[A-Za-z][A-Za-z0-9_-]{0,59}', but got: 'foo bar'\"}", + 400); + + // Rejects invalid tokenIds on delete + tester.assertResponse(request("/application/v4/tenant/scoober/token/foo+bar?fingerprint=ab:cd", DELETE).roles(Role.developer(tenantName)), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"tokenId must match '[A-Za-z][A-Za-z0-9_-]{0,59}', but got: 'foo bar'\"}", + 400); + + // Rejects invalid fingerprints on delete + tester.assertResponse(request("/application/v4/tenant/scoober/token/tokenid?fingerprint=ab:cdef", DELETE).roles(Role.developer(tenantName)), + "{\"error-code\":\"BAD_REQUEST\",\"message\":\"fingerPrint must match '([a-f0-9]{2}:)+[a-f0-9]{2}', but got: 'ab:cdef'\"}", + 400); + } + private ApplicationPackageBuilder prodBuilder() { return new ApplicationPackageBuilder() .withoutAthenzIdentity() diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/dataplanetoken/DataplaneTokenServiceTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/dataplanetoken/DataplaneTokenServiceTest.java new file mode 100644 index 00000000000..066eecc2c95 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/dataplanetoken/DataplaneTokenServiceTest.java @@ -0,0 +1,82 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.vespa.hosted.controller.restapi.dataplanetoken; + +import com.yahoo.config.provision.SystemName; +import com.yahoo.config.provision.TenantName; +import com.yahoo.vespa.hosted.controller.ControllerTester; +import com.yahoo.vespa.hosted.controller.api.integration.dataplanetoken.DataplaneToken; +import com.yahoo.vespa.hosted.controller.api.integration.dataplanetoken.DataplaneTokenVersions; +import com.yahoo.vespa.hosted.controller.api.integration.dataplanetoken.FingerPrint; +import com.yahoo.vespa.hosted.controller.api.integration.dataplanetoken.TokenId; +import com.yahoo.vespa.hosted.controller.api.role.SimplePrincipal; +import org.junit.jupiter.api.Test; + +import java.security.Principal; +import java.util.Collection; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class DataplaneTokenServiceTest { + private final ControllerTester tester = new ControllerTester(SystemName.Public); + private final DataplaneTokenService dataplaneTokenService = new DataplaneTokenService(tester.controller()); + private final TenantName tenantName = TenantName.from("tenant"); + Principal principal = new SimplePrincipal("user"); + private final TokenId tokenId = TokenId.of("myTokenId"); + + @Test + void generates_and_persists_token() { + DataplaneToken dataplaneToken = dataplaneTokenService.generateToken(tenantName, tokenId, principal); + List<DataplaneTokenVersions> dataplaneTokenVersions = dataplaneTokenService.listTokens(tenantName); + assertEquals(dataplaneToken.fingerPrint(), dataplaneTokenVersions.get(0).tokenVersions().get(0).fingerPrint()); + } + + @Test + void generating_new_token_appends() { + DataplaneToken dataplaneToken1 = dataplaneTokenService.generateToken(tenantName, tokenId, principal); + DataplaneToken dataplaneToken2 = dataplaneTokenService.generateToken(tenantName, tokenId, principal); + assertNotEquals(dataplaneToken1.fingerPrint(), dataplaneToken2.fingerPrint()); + + List<DataplaneTokenVersions> dataplaneTokenVersions = dataplaneTokenService.listTokens(tenantName); + List<FingerPrint> tokenFingerprints = dataplaneTokenVersions.stream() + .filter(token -> token.tokenId().equals(tokenId)) + .map(DataplaneTokenVersions::tokenVersions) + .flatMap(Collection::stream) + .map(DataplaneTokenVersions.Version::fingerPrint) + .toList(); + assertThat(tokenFingerprints).containsExactlyInAnyOrder(dataplaneToken1.fingerPrint(), dataplaneToken2.fingerPrint()); + } + + @Test + void delete_last_fingerprint_deletes_token() { + DataplaneToken dataplaneToken1 = dataplaneTokenService.generateToken(tenantName, tokenId, principal); + DataplaneToken dataplaneToken2 = dataplaneTokenService.generateToken(tenantName, tokenId, principal); + dataplaneTokenService.deleteToken(tenantName, tokenId, dataplaneToken1.fingerPrint()); + dataplaneTokenService.deleteToken(tenantName, tokenId, dataplaneToken2.fingerPrint()); + assertEquals(List.of(), dataplaneTokenService.listTokens(tenantName)); + } + + @Test + void deleting_nonexistent_fingerprint_throws() { + DataplaneToken dataplaneToken = dataplaneTokenService.generateToken(tenantName, tokenId, principal); + DataplaneToken dataplaneToken2 = dataplaneTokenService.generateToken(tenantName, tokenId, principal); + dataplaneTokenService.deleteToken(tenantName, tokenId, dataplaneToken.fingerPrint()); + + // Token currently contains value of "dataplaneToken2" + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> dataplaneTokenService.deleteToken(tenantName, tokenId, dataplaneToken.fingerPrint())); + assertEquals("Fingerprint does not exist: " + dataplaneToken.fingerPrint(), exception.getMessage()); + } + + @Test + void deleting_nonexistent_token_throws() { + DataplaneToken dataplaneToken = dataplaneTokenService.generateToken(tenantName, tokenId, principal); + dataplaneTokenService.deleteToken(tenantName, tokenId, dataplaneToken.fingerPrint()); + + // Token is created and deleted above, no longer exists + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> dataplaneTokenService.deleteToken(tenantName, tokenId, dataplaneToken.fingerPrint())); + assertEquals("Token does not exist: " + tokenId, exception.getMessage()); + } +} |