Skip to content

Commit 9c62026

Browse files
committed
An endpoint to list cloud encryption keys without the secrets
1 parent c87755b commit 9c62026

File tree

7 files changed

+279
-78
lines changed

7 files changed

+279
-78
lines changed

src/main/java/com/uid2/admin/Main.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@
5454
import io.vertx.core.Vertx;
5555
import io.vertx.core.VertxOptions;
5656
import io.vertx.core.http.HttpServerOptions;
57-
import io.vertx.core.http.impl.HttpUtils;
5857
import io.vertx.core.json.JsonObject;
5958
import org.slf4j.Logger;
6059
import org.slf4j.LoggerFactory;
@@ -201,16 +200,16 @@ public void run() {
201200

202201
CloudPath cloudEncryptionKeyMetadataPath = new CloudPath(config.getString(Const.Config.CloudEncryptionKeysMetadataPathProp));
203202
GlobalScope cloudEncryptionKeyGlobalScope = new GlobalScope(cloudEncryptionKeyMetadataPath);
204-
RotatingCloudEncryptionKeyProvider RotatingCloudEncryptionKeyProvider = new RotatingCloudEncryptionKeyProvider(cloudStorage, cloudEncryptionKeyGlobalScope);
205-
CloudEncryptionKeyStoreWriter cloudEncryptionKeyStoreWriter = new CloudEncryptionKeyStoreWriter(RotatingCloudEncryptionKeyProvider, fileManager, jsonWriter, versionGenerator, clock, cloudEncryptionKeyGlobalScope);
203+
RotatingCloudEncryptionKeyProvider rotatingCloudEncryptionKeyProvider = new RotatingCloudEncryptionKeyProvider(cloudStorage, cloudEncryptionKeyGlobalScope);
204+
CloudEncryptionKeyStoreWriter cloudEncryptionKeyStoreWriter = new CloudEncryptionKeyStoreWriter(rotatingCloudEncryptionKeyProvider, fileManager, jsonWriter, versionGenerator, clock, cloudEncryptionKeyGlobalScope);
206205
IKeyGenerator keyGenerator = new SecureKeyGenerator();
207-
CloudEncryptionKeyManager cloudEncryptionKeyManager = new CloudEncryptionKeyManager(RotatingCloudEncryptionKeyProvider, cloudEncryptionKeyStoreWriter,keyGenerator);
206+
CloudEncryptionKeyManager cloudEncryptionKeyManager = new CloudEncryptionKeyManager(rotatingCloudEncryptionKeyProvider, cloudEncryptionKeyStoreWriter,keyGenerator);
208207
try {
209-
RotatingCloudEncryptionKeyProvider.loadContent();
208+
rotatingCloudEncryptionKeyProvider.loadContent();
210209
} catch (CloudStorageException e) {
211210
if (e.getMessage().contains("The specified key does not exist")) {
212211
cloudEncryptionKeyStoreWriter.upload(new HashMap<>(), null);
213-
RotatingCloudEncryptionKeyProvider.loadContent();
212+
rotatingCloudEncryptionKeyProvider.loadContent();
214213
} else {
215214
throw e;
216215
}
@@ -261,9 +260,10 @@ public void run() {
261260
new SaltService(auth, writeLock, saltStoreWriter, saltProvider, saltRotation),
262261
new SiteService(auth, writeLock, siteStoreWriter, siteProvider, clientKeyProvider),
263262
new PartnerConfigService(auth, writeLock, partnerStoreWriter, partnerConfigProvider),
264-
new PrivateSiteDataRefreshService(auth, jobDispatcher, writeLock, config, RotatingCloudEncryptionKeyProvider),
263+
new PrivateSiteDataRefreshService(auth, jobDispatcher, writeLock, config, rotatingCloudEncryptionKeyProvider),
265264
new JobDispatcherService(auth, jobDispatcher),
266-
new SearchService(auth, clientKeyProvider, operatorKeyProvider)
265+
new SearchService(auth, clientKeyProvider, operatorKeyProvider),
266+
new CloudEncryptionKeyService(auth, rotatingCloudEncryptionKeyProvider)
267267
};
268268

269269

@@ -293,7 +293,7 @@ public void run() {
293293
config.getLong("cloud_encryption_key_activates_in_seconds"),
294294
config.getInteger("cloud_encryption_key_count_per_site")
295295
);
296-
RotatingCloudEncryptionKeyProvider.loadContent();
296+
rotatingCloudEncryptionKeyProvider.loadContent();
297297
}
298298

299299
/*
@@ -342,7 +342,7 @@ public void run() {
342342
CompletableFuture<Boolean> privateSiteDataSyncJobFuture = jobDispatcher.executeNextJob();
343343
privateSiteDataSyncJobFuture.get();
344344

345-
EncryptedFilesSyncJob encryptedFilesSyncJob = new EncryptedFilesSyncJob(config, writeLock,RotatingCloudEncryptionKeyProvider);
345+
EncryptedFilesSyncJob encryptedFilesSyncJob = new EncryptedFilesSyncJob(config, writeLock,rotatingCloudEncryptionKeyProvider);
346346
jobDispatcher.enqueue(encryptedFilesSyncJob);
347347
CompletableFuture<Boolean> encryptedFilesSyncJobFuture = jobDispatcher.executeNextJob();
348348
encryptedFilesSyncJobFuture.get();
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.uid2.admin.model;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
5+
import java.util.List;
6+
7+
public record CloudEncryptionKeyListResponse(
8+
@JsonProperty List<CloudEncryptionKeySummary> cloudEncryptionKeys
9+
) {}
10+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.uid2.admin.model;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import com.uid2.shared.model.CloudEncryptionKey;
5+
6+
public record CloudEncryptionKeySummary(
7+
@JsonProperty int id,
8+
@JsonProperty int siteId,
9+
@JsonProperty long activates,
10+
@JsonProperty long created
11+
) {
12+
public static CloudEncryptionKeySummary fromFullKey(CloudEncryptionKey key) {
13+
return new CloudEncryptionKeySummary(key.getId(), key.getSiteId(), key.getActivates(), key.getCreated());
14+
}
15+
}

src/main/java/com/uid2/admin/vertx/Endpoints.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ public enum Endpoints {
9898
API_SITE_APP_NAMES("/api/site/app_names"),
9999
API_SITE_UPDATE("/api/site/update"),
100100

101+
CLOUD_ENCRYPTION_KEY_LIST("/api/cloud-encryption-key/list"),
102+
101103
LOGIN("/login"),
102104
LOGOUT("/logout"),
103105
OPS_HEALTHCHECK("/ops/healthcheck"),
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package com.uid2.admin.vertx.service;
2+
3+
import com.fasterxml.jackson.core.JsonProcessingException;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import com.uid2.admin.auth.AdminAuthMiddleware;
6+
import com.uid2.admin.model.CloudEncryptionKeyListResponse;
7+
import com.uid2.admin.model.CloudEncryptionKeySummary;
8+
import com.uid2.admin.vertx.Endpoints;
9+
import com.uid2.shared.auth.Role;
10+
import com.uid2.shared.store.reader.RotatingCloudEncryptionKeyProvider;
11+
import com.uid2.shared.util.Mapper;
12+
import io.vertx.core.http.HttpHeaders;
13+
import io.vertx.ext.web.Router;
14+
import io.vertx.ext.web.RoutingContext;
15+
16+
import java.util.List;
17+
import java.util.stream.Collectors;
18+
import java.util.stream.Stream;
19+
20+
public class CloudEncryptionKeyService implements IService {
21+
private final AdminAuthMiddleware auth;
22+
private final RotatingCloudEncryptionKeyProvider keyProvider;
23+
private static final ObjectMapper OBJECT_MAPPER = Mapper.getInstance();
24+
25+
public CloudEncryptionKeyService(AdminAuthMiddleware auth, RotatingCloudEncryptionKeyProvider keyProvider) {
26+
this.auth = auth;
27+
this.keyProvider = keyProvider;
28+
}
29+
30+
@Override
31+
public void setupRoutes(Router router) {
32+
router.get(Endpoints.CLOUD_ENCRYPTION_KEY_LIST.toString()).handler(
33+
auth.handle(this::handleList, Role.MAINTAINER)
34+
);
35+
}
36+
37+
private void handleList(RoutingContext rc) {
38+
try {
39+
var keySummaries = keyProvider.getAll()
40+
.values()
41+
.stream()
42+
.map(CloudEncryptionKeySummary::fromFullKey)
43+
.toList();
44+
CloudEncryptionKeyListResponse response = new CloudEncryptionKeyListResponse(keySummaries);
45+
respondWithJson(rc, response);
46+
} catch (Exception e) {
47+
rc.fail(500, e);
48+
}
49+
}
50+
51+
private static void respondWithJson(RoutingContext rc, CloudEncryptionKeyListResponse response) throws JsonProcessingException {
52+
rc.response()
53+
.putHeader(HttpHeaders.CONTENT_TYPE, "application/json")
54+
.end(OBJECT_MAPPER.writeValueAsString(response));
55+
}
56+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.uid2.admin.vertx;
2+
3+
import com.fasterxml.jackson.core.type.TypeReference;
4+
import com.fasterxml.jackson.databind.ObjectMapper;
5+
import com.uid2.admin.model.CloudEncryptionKeyListResponse;
6+
import com.uid2.admin.model.CloudEncryptionKeySummary;
7+
import com.uid2.admin.vertx.service.CloudEncryptionKeyService;
8+
import com.uid2.admin.vertx.service.IService;
9+
import com.uid2.admin.vertx.test.ServiceTestBase;
10+
import com.uid2.shared.auth.Role;
11+
import com.uid2.shared.model.CloudEncryptionKey;
12+
import com.uid2.shared.util.Mapper;
13+
import io.vertx.core.Vertx;
14+
import io.vertx.junit5.VertxTestContext;
15+
import org.junit.jupiter.api.Test;
16+
17+
import java.util.List;
18+
19+
import static org.junit.jupiter.api.Assertions.*;
20+
21+
public class CloudEncryptionKeyServiceTest extends ServiceTestBase {
22+
private static final ObjectMapper OBJECT_MAPPER = Mapper.getInstance();
23+
24+
@Override
25+
protected IService createService() {
26+
return new CloudEncryptionKeyService(auth, cloudEncryptionKeyProvider);
27+
}
28+
29+
@Test
30+
public void testList_noKeys(Vertx vertx, VertxTestContext testContext) {
31+
fakeAuth(Role.MAINTAINER);
32+
var expected = new CloudEncryptionKeyListResponse(List.of());
33+
34+
get(vertx, testContext, Endpoints.CLOUD_ENCRYPTION_KEY_LIST, response -> {
35+
assertEquals(200, response.statusCode());
36+
37+
CloudEncryptionKeyListResponse actual = OBJECT_MAPPER.readValue(response.bodyAsString(), new TypeReference<>() {});
38+
assertEquals(expected, actual);
39+
40+
testContext.completeNow();
41+
});
42+
}
43+
44+
@Test
45+
public void testList_noAccess(Vertx vertx, VertxTestContext testContext) {
46+
get(vertx, testContext, Endpoints.CLOUD_ENCRYPTION_KEY_LIST, response -> {
47+
assertEquals(401, response.statusCode());
48+
testContext.completeNow();
49+
});
50+
}
51+
52+
@Test
53+
public void testList_withKeys(Vertx vertx, VertxTestContext testContext) {
54+
fakeAuth(Role.MAINTAINER);
55+
56+
CloudEncryptionKey key1 = new CloudEncryptionKey(1, 2, 100, 100, "secret 1");
57+
CloudEncryptionKey key2 = new CloudEncryptionKey(2, 2, 200, 100, "secret 2");
58+
59+
setCloudEncryptionKeys(key1, key2);
60+
61+
var expected = new CloudEncryptionKeyListResponse(List.of(
62+
new CloudEncryptionKeySummary(1, 2, 100, 100),
63+
new CloudEncryptionKeySummary(2, 2, 200, 100)
64+
));
65+
66+
get(vertx, testContext, Endpoints.CLOUD_ENCRYPTION_KEY_LIST, response -> {
67+
assertEquals(200, response.statusCode());
68+
69+
CloudEncryptionKeyListResponse actual = OBJECT_MAPPER.readValue(response.bodyAsString(), new TypeReference<>() {});
70+
assertEquals(expected, actual);
71+
72+
testContext.completeNow();
73+
});
74+
}
75+
}

0 commit comments

Comments
 (0)