diff --git a/src/main/java/com/uid2/admin/Main.java b/src/main/java/com/uid2/admin/Main.java index 1a324ae68..7b3234007 100644 --- a/src/main/java/com/uid2/admin/Main.java +++ b/src/main/java/com/uid2/admin/Main.java @@ -54,7 +54,6 @@ import io.vertx.core.Vertx; import io.vertx.core.VertxOptions; import io.vertx.core.http.HttpServerOptions; -import io.vertx.core.http.impl.HttpUtils; import io.vertx.core.json.JsonObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -201,16 +200,16 @@ public void run() { CloudPath cloudEncryptionKeyMetadataPath = new CloudPath(config.getString(Const.Config.CloudEncryptionKeysMetadataPathProp)); GlobalScope cloudEncryptionKeyGlobalScope = new GlobalScope(cloudEncryptionKeyMetadataPath); - RotatingCloudEncryptionKeyProvider RotatingCloudEncryptionKeyProvider = new RotatingCloudEncryptionKeyProvider(cloudStorage, cloudEncryptionKeyGlobalScope); - CloudEncryptionKeyStoreWriter cloudEncryptionKeyStoreWriter = new CloudEncryptionKeyStoreWriter(RotatingCloudEncryptionKeyProvider, fileManager, jsonWriter, versionGenerator, clock, cloudEncryptionKeyGlobalScope); + RotatingCloudEncryptionKeyProvider rotatingCloudEncryptionKeyProvider = new RotatingCloudEncryptionKeyProvider(cloudStorage, cloudEncryptionKeyGlobalScope); + CloudEncryptionKeyStoreWriter cloudEncryptionKeyStoreWriter = new CloudEncryptionKeyStoreWriter(rotatingCloudEncryptionKeyProvider, fileManager, jsonWriter, versionGenerator, clock, cloudEncryptionKeyGlobalScope); IKeyGenerator keyGenerator = new SecureKeyGenerator(); - CloudEncryptionKeyManager cloudEncryptionKeyManager = new CloudEncryptionKeyManager(RotatingCloudEncryptionKeyProvider, cloudEncryptionKeyStoreWriter,keyGenerator); + CloudEncryptionKeyManager cloudEncryptionKeyManager = new CloudEncryptionKeyManager(rotatingCloudEncryptionKeyProvider, cloudEncryptionKeyStoreWriter,keyGenerator); try { - RotatingCloudEncryptionKeyProvider.loadContent(); + rotatingCloudEncryptionKeyProvider.loadContent(); } catch (CloudStorageException e) { if (e.getMessage().contains("The specified key does not exist")) { cloudEncryptionKeyStoreWriter.upload(new HashMap<>(), null); - RotatingCloudEncryptionKeyProvider.loadContent(); + rotatingCloudEncryptionKeyProvider.loadContent(); } else { throw e; } @@ -261,9 +260,10 @@ public void run() { new SaltService(auth, writeLock, saltStoreWriter, saltProvider, saltRotation), new SiteService(auth, writeLock, siteStoreWriter, siteProvider, clientKeyProvider), new PartnerConfigService(auth, writeLock, partnerStoreWriter, partnerConfigProvider), - new PrivateSiteDataRefreshService(auth, jobDispatcher, writeLock, config, RotatingCloudEncryptionKeyProvider), + new PrivateSiteDataRefreshService(auth, jobDispatcher, writeLock, config, rotatingCloudEncryptionKeyProvider), new JobDispatcherService(auth, jobDispatcher), - new SearchService(auth, clientKeyProvider, operatorKeyProvider) + new SearchService(auth, clientKeyProvider, operatorKeyProvider), + new CloudEncryptionKeyService(auth, rotatingCloudEncryptionKeyProvider) }; @@ -293,7 +293,7 @@ public void run() { config.getLong("cloud_encryption_key_activates_in_seconds"), config.getInteger("cloud_encryption_key_count_per_site") ); - RotatingCloudEncryptionKeyProvider.loadContent(); + rotatingCloudEncryptionKeyProvider.loadContent(); } /* @@ -342,7 +342,7 @@ public void run() { CompletableFuture privateSiteDataSyncJobFuture = jobDispatcher.executeNextJob(); privateSiteDataSyncJobFuture.get(); - EncryptedFilesSyncJob encryptedFilesSyncJob = new EncryptedFilesSyncJob(config, writeLock,RotatingCloudEncryptionKeyProvider); + EncryptedFilesSyncJob encryptedFilesSyncJob = new EncryptedFilesSyncJob(config, writeLock,rotatingCloudEncryptionKeyProvider); jobDispatcher.enqueue(encryptedFilesSyncJob); CompletableFuture encryptedFilesSyncJobFuture = jobDispatcher.executeNextJob(); encryptedFilesSyncJobFuture.get(); diff --git a/src/main/java/com/uid2/admin/model/CloudEncryptionKeyListResponse.java b/src/main/java/com/uid2/admin/model/CloudEncryptionKeyListResponse.java new file mode 100644 index 000000000..e62b48997 --- /dev/null +++ b/src/main/java/com/uid2/admin/model/CloudEncryptionKeyListResponse.java @@ -0,0 +1,10 @@ +package com.uid2.admin.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +public record CloudEncryptionKeyListResponse( + @JsonProperty List cloudEncryptionKeys +) {} + diff --git a/src/main/java/com/uid2/admin/model/CloudEncryptionKeySummary.java b/src/main/java/com/uid2/admin/model/CloudEncryptionKeySummary.java new file mode 100644 index 000000000..89ef734d7 --- /dev/null +++ b/src/main/java/com/uid2/admin/model/CloudEncryptionKeySummary.java @@ -0,0 +1,15 @@ +package com.uid2.admin.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.uid2.shared.model.CloudEncryptionKey; + +public record CloudEncryptionKeySummary( + @JsonProperty int id, + @JsonProperty int siteId, + @JsonProperty long activates, + @JsonProperty long created +) { + public static CloudEncryptionKeySummary fromFullKey(CloudEncryptionKey key) { + return new CloudEncryptionKeySummary(key.getId(), key.getSiteId(), key.getActivates(), key.getCreated()); + } +} diff --git a/src/main/java/com/uid2/admin/vertx/Endpoints.java b/src/main/java/com/uid2/admin/vertx/Endpoints.java index 7d63a4e25..ddbe14008 100644 --- a/src/main/java/com/uid2/admin/vertx/Endpoints.java +++ b/src/main/java/com/uid2/admin/vertx/Endpoints.java @@ -98,6 +98,8 @@ public enum Endpoints { API_SITE_APP_NAMES("/api/site/app_names"), API_SITE_UPDATE("/api/site/update"), + CLOUD_ENCRYPTION_KEY_LIST("/api/cloud-encryption-key/list"), + LOGIN("/login"), LOGOUT("/logout"), OPS_HEALTHCHECK("/ops/healthcheck"), diff --git a/src/main/java/com/uid2/admin/vertx/service/CloudEncryptionKeyService.java b/src/main/java/com/uid2/admin/vertx/service/CloudEncryptionKeyService.java new file mode 100644 index 000000000..de21b7d3a --- /dev/null +++ b/src/main/java/com/uid2/admin/vertx/service/CloudEncryptionKeyService.java @@ -0,0 +1,56 @@ +package com.uid2.admin.vertx.service; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.uid2.admin.auth.AdminAuthMiddleware; +import com.uid2.admin.model.CloudEncryptionKeyListResponse; +import com.uid2.admin.model.CloudEncryptionKeySummary; +import com.uid2.admin.vertx.Endpoints; +import com.uid2.shared.auth.Role; +import com.uid2.shared.store.reader.RotatingCloudEncryptionKeyProvider; +import com.uid2.shared.util.Mapper; +import io.vertx.core.http.HttpHeaders; +import io.vertx.ext.web.Router; +import io.vertx.ext.web.RoutingContext; + +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class CloudEncryptionKeyService implements IService { + private final AdminAuthMiddleware auth; + private final RotatingCloudEncryptionKeyProvider keyProvider; + private static final ObjectMapper OBJECT_MAPPER = Mapper.getInstance(); + + public CloudEncryptionKeyService(AdminAuthMiddleware auth, RotatingCloudEncryptionKeyProvider keyProvider) { + this.auth = auth; + this.keyProvider = keyProvider; + } + + @Override + public void setupRoutes(Router router) { + router.get(Endpoints.CLOUD_ENCRYPTION_KEY_LIST.toString()).handler( + auth.handle(this::handleList, Role.MAINTAINER) + ); + } + + private void handleList(RoutingContext rc) { + try { + var keySummaries = keyProvider.getAll() + .values() + .stream() + .map(CloudEncryptionKeySummary::fromFullKey) + .toList(); + CloudEncryptionKeyListResponse response = new CloudEncryptionKeyListResponse(keySummaries); + respondWithJson(rc, response); + } catch (Exception e) { + rc.fail(500, e); + } + } + + private static void respondWithJson(RoutingContext rc, CloudEncryptionKeyListResponse response) throws JsonProcessingException { + rc.response() + .putHeader(HttpHeaders.CONTENT_TYPE, "application/json") + .end(OBJECT_MAPPER.writeValueAsString(response)); + } +} diff --git a/src/test/java/com/uid2/admin/vertx/CloudEncryptionKeyServiceTest.java b/src/test/java/com/uid2/admin/vertx/CloudEncryptionKeyServiceTest.java new file mode 100644 index 000000000..0adb109ee --- /dev/null +++ b/src/test/java/com/uid2/admin/vertx/CloudEncryptionKeyServiceTest.java @@ -0,0 +1,75 @@ +package com.uid2.admin.vertx; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.uid2.admin.model.CloudEncryptionKeyListResponse; +import com.uid2.admin.model.CloudEncryptionKeySummary; +import com.uid2.admin.vertx.service.CloudEncryptionKeyService; +import com.uid2.admin.vertx.service.IService; +import com.uid2.admin.vertx.test.ServiceTestBase; +import com.uid2.shared.auth.Role; +import com.uid2.shared.model.CloudEncryptionKey; +import com.uid2.shared.util.Mapper; +import io.vertx.core.Vertx; +import io.vertx.junit5.VertxTestContext; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +public class CloudEncryptionKeyServiceTest extends ServiceTestBase { + private static final ObjectMapper OBJECT_MAPPER = Mapper.getInstance(); + + @Override + protected IService createService() { + return new CloudEncryptionKeyService(auth, cloudEncryptionKeyProvider); + } + + @Test + public void testList_noKeys(Vertx vertx, VertxTestContext testContext) { + fakeAuth(Role.MAINTAINER); + var expected = new CloudEncryptionKeyListResponse(List.of()); + + get(vertx, testContext, Endpoints.CLOUD_ENCRYPTION_KEY_LIST, response -> { + assertEquals(200, response.statusCode()); + + CloudEncryptionKeyListResponse actual = OBJECT_MAPPER.readValue(response.bodyAsString(), new TypeReference<>() {}); + assertEquals(expected, actual); + + testContext.completeNow(); + }); + } + + @Test + public void testList_noAccess(Vertx vertx, VertxTestContext testContext) { + get(vertx, testContext, Endpoints.CLOUD_ENCRYPTION_KEY_LIST, response -> { + assertEquals(401, response.statusCode()); + testContext.completeNow(); + }); + } + + @Test + public void testList_withKeys(Vertx vertx, VertxTestContext testContext) { + fakeAuth(Role.MAINTAINER); + + CloudEncryptionKey key1 = new CloudEncryptionKey(1, 2, 100, 100, "secret 1"); + CloudEncryptionKey key2 = new CloudEncryptionKey(2, 2, 200, 100, "secret 2"); + + setCloudEncryptionKeys(key1, key2); + + var expected = new CloudEncryptionKeyListResponse(List.of( + new CloudEncryptionKeySummary(1, 2, 100, 100), + new CloudEncryptionKeySummary(2, 2, 200, 100) + )); + + get(vertx, testContext, Endpoints.CLOUD_ENCRYPTION_KEY_LIST, response -> { + assertEquals(200, response.statusCode()); + + CloudEncryptionKeyListResponse actual = OBJECT_MAPPER.readValue(response.bodyAsString(), new TypeReference<>() {}); + assertEquals(expected, actual); + + testContext.completeNow(); + }); + } +} diff --git a/src/test/java/com/uid2/admin/vertx/test/ServiceTestBase.java b/src/test/java/com/uid2/admin/vertx/test/ServiceTestBase.java index 78ca6661f..fc9b2e9cc 100644 --- a/src/test/java/com/uid2/admin/vertx/test/ServiceTestBase.java +++ b/src/test/java/com/uid2/admin/vertx/test/ServiceTestBase.java @@ -10,6 +10,7 @@ import com.uid2.admin.legacy.RotatingLegacyClientKeyProvider; import com.uid2.admin.managers.KeysetManager; import com.uid2.admin.secret.IEncryptionKeyManager; +import com.uid2.admin.vertx.Endpoints; import com.uid2.shared.model.*; import com.uid2.shared.secret.IKeyGenerator; import com.uid2.admin.secret.IKeysetKeyManager; @@ -38,6 +39,7 @@ import io.vertx.ext.web.handler.AuthenticationHandler; import io.vertx.junit5.VertxExtension; import io.vertx.junit5.VertxTestContext; +import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.extension.ExtendWith; @@ -58,56 +60,99 @@ public abstract class ServiceTestBase { protected final WriteLock writeLock = new WriteLock(); protected AdminAuthMiddleware auth; - @Mock private TokenRefreshHandler tokenRefreshHandler; - @Mock private IdTokenVerifier idTokenVerifier; - @Mock private AccessTokenVerifier accessTokenVerifier; - @Mock private Jwt jwt; - @Mock protected AuthenticationHandler authHandler; - @Mock protected AuthProvider authProvider; - - @Mock protected StoreWriter storeWriter; - @Mock protected LegacyClientKeyStoreWriter clientKeyStoreWriter; - @Mock protected EncryptionKeyStoreWriter encryptionKeyStoreWriter; - @Mock protected KeysetKeyStoreWriter keysetKeyStoreWriter; - @Mock protected ClientSideKeypairStoreWriter keypairStoreWriter; - @Mock protected ServiceStoreWriter serviceStoreWriter; - @Mock protected ServiceLinkStoreWriter serviceLinkStoreWriter; - @Mock protected KeyAclStoreWriter keyAclStoreWriter; - @Mock protected KeysetStoreWriter keysetStoreWriter; - @Mock protected AdminKeysetWriter adminKeysetWriter; - @Mock protected OperatorKeyStoreWriter operatorKeyStoreWriter; - @Mock protected EnclaveStoreWriter enclaveStoreWriter; - @Mock protected SaltStoreWriter saltStoreWriter; - @Mock protected PartnerStoreWriter partnerStoreWriter; - - @Mock protected IEncryptionKeyManager keyManager; - @Mock protected KeysetManager keysetManager; - @Mock protected IKeysetKeyManager keysetKeyManager; - @Mock protected RotatingSiteStore siteProvider; - @Mock protected RotatingLegacyClientKeyProvider clientKeyProvider; - @Mock protected RotatingKeyStore keyProvider; - @Mock protected IKeyStore.IKeyStoreSnapshot keyProviderSnapshot; - @Mock protected KeysetKeyStoreSnapshot keysetKeyProviderSnapshot; - @Mock protected RotatingKeyAclProvider keyAclProvider; - @Mock protected RotatingKeysetProvider keysetProvider; - @Mock protected RotatingAdminKeysetStore adminKeysetProvider; - @Mock protected RotatingKeysetKeyStore keysetKeyProvider; - @Mock protected RotatingClientSideKeypairStore keypairProvider; - @Mock protected RotatingServiceStore serviceProvider; - @Mock protected RotatingServiceLinkStore serviceLinkProvider; - @Mock protected KeysetSnapshot keysetSnapshot; - @Mock protected AdminKeysetSnapshot adminKeysetSnapshot; - @Mock protected AclSnapshot keyAclProviderSnapshot; - @Mock protected RotatingOperatorKeyProvider operatorKeyProvider; - @Mock protected EnclaveIdentifierProvider enclaveIdentifierProvider; - @Mock protected IKeyGenerator keyGenerator; - @Mock protected KeyHasher keyHasher; - @Mock protected Clock clock; + @Mock + private TokenRefreshHandler tokenRefreshHandler; + @Mock + private IdTokenVerifier idTokenVerifier; + @Mock + private AccessTokenVerifier accessTokenVerifier; + @Mock + private Jwt jwt; + @Mock + protected AuthenticationHandler authHandler; + @Mock + protected AuthProvider authProvider; + + @Mock + protected StoreWriter storeWriter; + @Mock + protected LegacyClientKeyStoreWriter clientKeyStoreWriter; + @Mock + protected EncryptionKeyStoreWriter encryptionKeyStoreWriter; + @Mock + protected KeysetKeyStoreWriter keysetKeyStoreWriter; + @Mock + protected ClientSideKeypairStoreWriter keypairStoreWriter; + @Mock + protected ServiceStoreWriter serviceStoreWriter; + @Mock + protected ServiceLinkStoreWriter serviceLinkStoreWriter; + @Mock + protected KeyAclStoreWriter keyAclStoreWriter; + @Mock + protected KeysetStoreWriter keysetStoreWriter; + @Mock + protected AdminKeysetWriter adminKeysetWriter; + @Mock + protected OperatorKeyStoreWriter operatorKeyStoreWriter; + @Mock + protected EnclaveStoreWriter enclaveStoreWriter; + @Mock + protected SaltStoreWriter saltStoreWriter; + @Mock + protected PartnerStoreWriter partnerStoreWriter; + + @Mock + protected IEncryptionKeyManager keyManager; + @Mock + protected KeysetManager keysetManager; + @Mock + protected IKeysetKeyManager keysetKeyManager; + @Mock + protected RotatingSiteStore siteProvider; + @Mock + protected RotatingLegacyClientKeyProvider clientKeyProvider; + @Mock + protected RotatingKeyStore keyProvider; + @Mock + protected IKeyStore.IKeyStoreSnapshot keyProviderSnapshot; + @Mock + protected KeysetKeyStoreSnapshot keysetKeyProviderSnapshot; + @Mock + protected RotatingKeyAclProvider keyAclProvider; + @Mock + protected RotatingKeysetProvider keysetProvider; + @Mock + protected RotatingAdminKeysetStore adminKeysetProvider; + @Mock + protected RotatingKeysetKeyStore keysetKeyProvider; + @Mock + protected RotatingClientSideKeypairStore keypairProvider; + @Mock + protected RotatingServiceStore serviceProvider; + @Mock + protected RotatingCloudEncryptionKeyProvider cloudEncryptionKeyProvider; + @Mock + protected RotatingServiceLinkStore serviceLinkProvider; + @Mock + protected KeysetSnapshot keysetSnapshot; + @Mock + protected AdminKeysetSnapshot adminKeysetSnapshot; + @Mock + protected AclSnapshot keyAclProviderSnapshot; + @Mock + protected RotatingOperatorKeyProvider operatorKeyProvider; + @Mock + protected EnclaveIdentifierProvider enclaveIdentifierProvider; + @Mock + protected IKeyGenerator keyGenerator; + @Mock + protected KeyHasher keyHasher; + @Mock + protected Clock clock; final Map> roleToOktaGroups = new EnumMap<>(Role.class); - static final Set CUSTOM_OKTA_SCOPE_ROLES = Arrays.stream(OktaCustomScope.values()) - .map(OktaCustomScope::getRole) - .collect(Collectors.toSet()); + static final Set CUSTOM_OKTA_SCOPE_ROLES = Arrays.stream(OktaCustomScope.values()).map(OktaCustomScope::getRole).collect(Collectors.toSet()); @BeforeEach public void deployVerticle(Vertx vertx, VertxTestContext testContext) throws Throwable { @@ -118,8 +163,7 @@ public void deployVerticle(Vertx vertx, VertxTestContext testContext) throws Thr when(keyAclProvider.getSnapshot()).thenReturn(keyAclProviderSnapshot); when(keysetProvider.getSnapshot()).thenReturn(keysetSnapshot); when(adminKeysetProvider.getSnapshot()).thenReturn(adminKeysetSnapshot); - when(siteProvider.getSite(anyInt())).then((i) -> siteProvider.getAllSites().stream() - .filter(s -> s.getId() == (Integer) i.getArgument(0)).findFirst().orElse(null)); + when(siteProvider.getSite(anyInt())).then((i) -> siteProvider.getAllSites().stream().filter(s -> s.getId() == (Integer) i.getArgument(0)).findFirst().orElse(null)); when(keyGenerator.generateRandomKey(anyInt())).thenReturn(new byte[]{1, 2, 3, 4, 5, 6}); when(keyGenerator.generateRandomKeyString(anyInt())).thenReturn(Utils.toBase64String(new byte[]{1, 2, 3, 4, 5, 6})); when(keyGenerator.generateFormattedKeyString(anyInt())).thenReturn("abcdef.abcdefabcdefabcdef"); @@ -135,10 +179,8 @@ public void deployVerticle(Vertx vertx, VertxTestContext testContext) throws Thr })).when(tokenRefreshHandler).handle(any()); when(idTokenVerifier.decode(anyString(), any())).thenReturn(jwt); when(accessTokenVerifier.decode(anyString())).thenReturn(jwt); - config.put(AdminConst.ROLE_OKTA_GROUP_MAP_MAINTAINER, String.join(",", OktaGroup.DEVELOPER.getName(), - OktaGroup.DEVELOPER_ELEVATED.getName(), OktaGroup.ADMIN.getName())); - config.put(AdminConst.ROLE_OKTA_GROUP_MAP_PRIVILEGED, String.join(",", OktaGroup.DEVELOPER_ELEVATED.getName(), - OktaGroup.ADMIN.getName())); + config.put(AdminConst.ROLE_OKTA_GROUP_MAP_MAINTAINER, String.join(",", OktaGroup.DEVELOPER.getName(), OktaGroup.DEVELOPER_ELEVATED.getName(), OktaGroup.ADMIN.getName())); + config.put(AdminConst.ROLE_OKTA_GROUP_MAP_PRIVILEGED, String.join(",", OktaGroup.DEVELOPER_ELEVATED.getName(), OktaGroup.ADMIN.getName())); config.put(AdminConst.ROLE_OKTA_GROUP_MAP_SUPER_USER, OktaGroup.ADMIN.getName()); auth = new AdminAuthMiddleware(authProvider, config); roleToOktaGroups.put(Role.MAINTAINER, List.of(OktaGroup.DEVELOPER)); @@ -163,10 +205,7 @@ private String getUrlForEndpoint(String endpoint) { protected void fakeAuth(Role role) { if (CUSTOM_OKTA_SCOPE_ROLES.contains(role)) { - String group = Arrays.stream(OktaCustomScope.values()) - .filter(scope -> scope.getRole() == role) - .findFirst().orElse(OktaCustomScope.INVALID) - .getName(); + String group = Arrays.stream(OktaCustomScope.values()).filter(scope -> scope.getRole() == role).findFirst().orElse(OktaCustomScope.INVALID).getName(); doAnswer((invocationOnMock -> { ((RoutingContext) invocationOnMock.getArgument(0)).request().headers().add("Authorization", "bearer token"); ((RoutingContext) invocationOnMock.getArgument(0)).next(); @@ -174,26 +213,23 @@ protected void fakeAuth(Role role) { })).when(tokenRefreshHandler).handle(any()); when(jwt.getClaims()).thenReturn(Map.of("scp", Collections.singletonList(group), "environment", "local")); } else { - List groups = roleToOktaGroups.getOrDefault(role, List.of(OktaGroup.INVALID)) - .stream() - .map(OktaGroup::toString) - .collect(Collectors.toList()); + List groups = roleToOktaGroups.getOrDefault(role, List.of(OktaGroup.INVALID)).stream().map(OktaGroup::toString).collect(Collectors.toList()); when(jwt.getClaims()).thenReturn(Map.of("groups", groups, "environment", "local")); } } protected void get(Vertx vertx, VertxTestContext testContext, String endpoint, TestHandler> handler) { WebClient client = WebClient.create(vertx); - client.getAbs(getUrlForEndpoint(endpoint)) - .send() - .onComplete(testContext.succeeding(response -> testContext.verify(() -> handler.handle(response)))); + client.getAbs(getUrlForEndpoint(endpoint)).send().onComplete(testContext.succeeding(response -> testContext.verify(() -> handler.handle(response)))); + } + + protected void get(Vertx vertx, VertxTestContext testContext, Endpoints endpoint, TestHandler> handler) { + get(vertx, testContext, endpoint.toString(), handler); } protected void post(Vertx vertx, VertxTestContext testContext, String endpoint, String body, TestHandler> handler) { WebClient client = WebClient.create(vertx); - client.postAbs(getUrlForEndpoint(endpoint)) - .sendBuffer(body != null ? Buffer.buffer(body) : null) - .onComplete(testContext.succeeding(response -> testContext.verify(() -> handler.handle(response)))); + client.postAbs(getUrlForEndpoint(endpoint)).sendBuffer(body != null ? Buffer.buffer(body) : null).onComplete(testContext.succeeding(response -> testContext.verify(() -> handler.handle(response)))); } protected void postWithoutBody(Vertx vertx, VertxTestContext testContext, String endpoint, TestHandler> handler) { @@ -211,6 +247,13 @@ protected void setServiceLinks(ServiceLink... serviceLinks) { when(serviceLinkProvider.getAllServiceLinks()).thenReturn(Arrays.asList(serviceLinks)); } + protected void setCloudEncryptionKeys(CloudEncryptionKey... cloudEncryptionKeys) { + var keysById = Arrays + .stream(cloudEncryptionKeys) + .collect(Collectors.toMap(CloudEncryptionKey::getId, x -> x)); + when(cloudEncryptionKeyProvider.getAll()).thenReturn(keysById); + } + protected void setSites(Site... sites) { when(siteProvider.getAllSites()).thenReturn(Arrays.asList(sites)); }