Skip to content

Commit 31edcfb

Browse files
authored
Merge pull request #404 from IABTechLab/aul-UID2-4573-refactor-existing-cloud-encryption-key-stuff-3
Creating cloud encryption keys based on operator keys, not sites
2 parents 23f3a7d + 1678148 commit 31edcfb

File tree

5 files changed

+106
-56
lines changed

5 files changed

+106
-56
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,7 @@ public void run() {
268268
new PrivateSiteDataRefreshService(auth, jobDispatcher, writeLock, config, rotatingCloudEncryptionKeyProvider),
269269
new JobDispatcherService(auth, jobDispatcher),
270270
new SearchService(auth, clientKeyProvider, operatorKeyProvider),
271-
new CloudEncryptionKeyService(auth, rotatingCloudEncryptionKeyProvider, cloudEncryptionKeyStoreWriter, siteProvider, cloudEncryptionKeyRotationStrategy)
271+
new CloudEncryptionKeyService(auth, rotatingCloudEncryptionKeyProvider, cloudEncryptionKeyStoreWriter, operatorKeyProvider, cloudEncryptionKeyRotationStrategy)
272272
};
273273

274274

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.uid2.admin.cloudencryption;
2+
3+
import com.uid2.admin.store.Clock;
4+
import com.uid2.shared.model.CloudEncryptionKey;
5+
6+
import java.util.Collection;
7+
8+
public class CloudEncryptionKeyGenerator {
9+
private final Clock clock;
10+
private final CloudSecretGenerator secretGenerator;
11+
private final KeyIdGenerator idGenerator;
12+
13+
public CloudEncryptionKeyGenerator(
14+
Clock clock,
15+
CloudSecretGenerator secretGenerator,
16+
Collection<CloudEncryptionKey> existingKeys) {
17+
this.clock = clock;
18+
this.secretGenerator = secretGenerator;
19+
this.idGenerator = new KeyIdGenerator(existingKeys);
20+
}
21+
22+
public CloudEncryptionKey makeNewKey(Integer siteId) {
23+
var nowSeconds = clock.getEpochSecond();
24+
var keyId = idGenerator.nextId();
25+
var secret = secretGenerator.generate();
26+
return new CloudEncryptionKey(keyId, siteId, nowSeconds, nowSeconds, secret);
27+
}
28+
29+
private static class KeyIdGenerator {
30+
private int lastId;
31+
32+
public KeyIdGenerator(Collection<CloudEncryptionKey> existingKeys) {
33+
this.lastId = existingKeys.stream().map(CloudEncryptionKey::getId).max(Integer::compareTo).orElse(0);
34+
}
35+
36+
public int nextId() {
37+
return ++lastId;
38+
}
39+
}
40+
}

src/main/java/com/uid2/admin/cloudencryption/CloudKeyRotationStrategy.java

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
import com.google.common.collect.Streams;
44
import com.uid2.admin.store.Clock;
5+
import com.uid2.shared.auth.OperatorKey;
56
import com.uid2.shared.model.CloudEncryptionKey;
6-
import com.uid2.shared.model.Site;
77

88
import java.util.Collection;
99
import java.util.Map;
@@ -27,48 +27,29 @@ public CloudKeyRotationStrategy(
2727
}
2828

2929
public Set<CloudEncryptionKey> computeDesiredKeys(
30-
Collection<CloudEncryptionKey> existingKeys,
31-
Collection<Site> sites
30+
Collection<CloudEncryptionKey> existingCloudKeys,
31+
Collection<OperatorKey> operatorKeys
3232
) {
33-
var idGenerator = new KeyIdGenerator(existingKeys);
34-
Map<Integer, Set<CloudEncryptionKey>> existingKeysBySite = existingKeys
33+
var keyGenerator = new CloudEncryptionKeyGenerator(clock, secretGenerator, existingCloudKeys);
34+
Map<Integer, Set<CloudEncryptionKey>> existingKeysBySite = existingCloudKeys
3535
.stream()
3636
.collect(Collectors.groupingBy(CloudEncryptionKey::getSiteId, Collectors.toSet()));
3737

38-
return sites
38+
return operatorKeys
3939
.stream()
40-
.map(Site::getId)
40+
.map(OperatorKey::getSiteId)
4141
.distinct()
42-
.flatMap(siteId -> desiredKeysForSite(siteId, idGenerator, existingKeysBySite.getOrDefault(siteId, Set.of())))
42+
.flatMap(siteId -> desiredKeysForSite(siteId, keyGenerator, existingKeysBySite.getOrDefault(siteId, Set.of())))
4343
.collect(Collectors.toSet());
4444
}
4545

4646
private Stream<CloudEncryptionKey> desiredKeysForSite(
4747
Integer siteId,
48-
KeyIdGenerator idGenerator,
48+
CloudEncryptionKeyGenerator keyGenerator,
4949
Set<CloudEncryptionKey> existingKeys
5050
) {
5151
var existingKeysToRetain = keyRetentionStrategy.selectKeysToRetain(existingKeys);
52-
var newKey = makeNewKey(siteId, idGenerator);
52+
var newKey = keyGenerator.makeNewKey(siteId);
5353
return Streams.concat(existingKeysToRetain.stream(), Stream.of(newKey));
5454
}
55-
56-
private CloudEncryptionKey makeNewKey(Integer siteId, KeyIdGenerator idGenerator) {
57-
var nowSeconds = clock.getEpochSecond();
58-
var keyId = idGenerator.nextId();
59-
var secret = secretGenerator.generate();
60-
return new CloudEncryptionKey(keyId, siteId, nowSeconds, nowSeconds, secret);
61-
}
62-
63-
private static class KeyIdGenerator {
64-
private int lastId;
65-
66-
public KeyIdGenerator(Collection<CloudEncryptionKey> existingKeys) {
67-
this.lastId = existingKeys.stream().map(CloudEncryptionKey::getId).max(Integer::compareTo).orElse(0);
68-
}
69-
70-
public int nextId() {
71-
return ++lastId;
72-
}
73-
}
7455
}

src/main/java/com/uid2/admin/vertx/service/CloudEncryptionKeyService.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
import com.uid2.admin.store.writer.CloudEncryptionKeyStoreWriter;
1010
import com.uid2.admin.vertx.Endpoints;
1111
import com.uid2.shared.auth.Role;
12+
import com.uid2.shared.auth.RotatingOperatorKeyProvider;
1213
import com.uid2.shared.model.CloudEncryptionKey;
1314
import com.uid2.shared.store.reader.RotatingCloudEncryptionKeyProvider;
14-
import com.uid2.shared.store.reader.RotatingSiteStore;
1515
import com.uid2.shared.util.Mapper;
1616
import io.vertx.core.http.HttpHeaders;
1717
import io.vertx.ext.web.Router;
@@ -24,7 +24,7 @@
2424
public class CloudEncryptionKeyService implements IService {
2525
private final AdminAuthMiddleware auth;
2626
private final RotatingCloudEncryptionKeyProvider keyProvider;
27-
private final RotatingSiteStore siteProvider;
27+
private final RotatingOperatorKeyProvider operatorKeyProvider;
2828
private final CloudEncryptionKeyStoreWriter keyWriter;
2929
private final CloudKeyRotationStrategy rotationStrategy;
3030
private static final ObjectMapper OBJECT_MAPPER = Mapper.getInstance();
@@ -33,12 +33,12 @@ public CloudEncryptionKeyService(
3333
AdminAuthMiddleware auth,
3434
RotatingCloudEncryptionKeyProvider keyProvider,
3535
CloudEncryptionKeyStoreWriter keyWriter,
36-
RotatingSiteStore siteProvider,
36+
RotatingOperatorKeyProvider operatorKeyProvider,
3737
CloudKeyRotationStrategy rotationStrategy
3838
) {
3939
this.auth = auth;
4040
this.keyProvider = keyProvider;
41-
this.siteProvider = siteProvider;
41+
this.operatorKeyProvider = operatorKeyProvider;
4242
this.keyWriter = keyWriter;
4343
this.rotationStrategy = rotationStrategy;
4444
}
@@ -57,11 +57,11 @@ public void setupRoutes(Router router) {
5757
private void handleRotate(RoutingContext rc) {
5858
try {
5959
keyProvider.loadContent();
60-
siteProvider.loadContent();
61-
var allSites = siteProvider.getAllSites();
60+
operatorKeyProvider.loadContent(operatorKeyProvider.getMetadata());
61+
var operatorKeys = operatorKeyProvider.getAll();
6262
var existingKeys = keyProvider.getAll().values();
6363

64-
var desiredKeys = rotationStrategy.computeDesiredKeys(existingKeys, allSites);
64+
var desiredKeys = rotationStrategy.computeDesiredKeys(existingKeys, operatorKeys);
6565
writeKeys(desiredKeys);
6666

6767
rc.response().end();

src/test/java/com/uid2/admin/vertx/CloudEncryptionKeyServiceTest.java

Lines changed: 48 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010
import com.uid2.admin.vertx.service.CloudEncryptionKeyService;
1111
import com.uid2.admin.vertx.service.IService;
1212
import com.uid2.admin.vertx.test.ServiceTestBase;
13+
import com.uid2.shared.auth.OperatorKey;
1314
import com.uid2.shared.auth.Role;
1415
import com.uid2.shared.model.CloudEncryptionKey;
15-
import com.uid2.shared.model.Site;
1616
import com.uid2.shared.util.Mapper;
1717
import io.vertx.core.Vertx;
1818
import io.vertx.core.buffer.Buffer;
@@ -24,8 +24,7 @@
2424
import java.util.Map;
2525

2626
import static org.junit.jupiter.api.Assertions.*;
27-
import static org.mockito.Mockito.verify;
28-
import static org.mockito.Mockito.when;
27+
import static org.mockito.Mockito.*;
2928

3029
public class CloudEncryptionKeyServiceTest extends ServiceTestBase {
3130
private static final ObjectMapper OBJECT_MAPPER = Mapper.getInstance();
@@ -38,12 +37,12 @@ public class CloudEncryptionKeyServiceTest extends ServiceTestBase {
3837
private final int keyId2 = 2;
3938
private final int keyId3 = 3;
4039
private final int keyId4 = 4;
41-
private final String siteName1 = "Site 1";
42-
private final Site site1 = new Site(siteId1, siteName1, true);
40+
private final OperatorKey operator1 = testOperatorKey(siteId1, "one");
41+
private final OperatorKey operator2 = testOperatorKey(siteId1, "two");
4342
private final String secret1 = "secret 1";
4443
private final String secret2 = "secret 2";
4544
private final String secret3 = "secret 3";
46-
private final String secret4 = "secret4";
45+
private final String secret4 = "secret 4";
4746

4847
@Override
4948
protected IService createService() {
@@ -54,7 +53,7 @@ protected IService createService() {
5453
auth,
5554
cloudEncryptionKeyProvider,
5655
cloudEncryptionKeyStoreWriter,
57-
siteProvider,
56+
operatorKeyProvider,
5857
rotationStrategy
5958
);
6059
}
@@ -127,30 +126,27 @@ public void testRotate_canBeRotatedBySecretRotationJob(Vertx vertx, VertxTestCon
127126
}
128127

129128
@Test
130-
public void testRotate_noSitesDoesNothing(Vertx vertx, VertxTestContext testContext) {
129+
public void testRotate_noOperatorsNoKeys(Vertx vertx, VertxTestContext testContext) {
131130
fakeAuth(Role.MAINTAINER);
132131

133132
setCloudEncryptionKeys();
134-
setSites();
133+
setOperatorKeys();
135134

136135
post(vertx, testContext, Endpoints.CLOUD_ENCRYPTION_KEY_ROTATE, null, rotateResponse -> {
137136
assertEquals(200, rotateResponse.statusCode());
138137

139-
get(vertx, testContext, Endpoints.CLOUD_ENCRYPTION_KEY_LIST, listResponse -> {
140-
assertEquals(200, listResponse.statusCode());
141-
assertEquals(noKeys, parseKeyListResponse(listResponse));
138+
verify(cloudEncryptionKeyStoreWriter).upload(Map.of(), null);
142139

143-
testContext.completeNow();
144-
});
140+
testContext.completeNow();
145141
});
146142
}
147143

148144
@Test
149-
public void testRotate_forSiteWithNoKeysCreatesKey(Vertx vertx, VertxTestContext testContext) {
145+
public void testRotate_forOperatorSiteWithNoKeysCreatesKey(Vertx vertx, VertxTestContext testContext) {
150146
fakeAuth(Role.MAINTAINER);
151147

152148
setCloudEncryptionKeys();
153-
setSites(site1);
149+
setOperatorKeys(operator1);
154150
when(cloudSecretGenerator.generate()).thenReturn(secret1);
155151
when(clock.getEpochSecond()).thenReturn(now);
156152

@@ -166,7 +162,26 @@ siteId1, new CloudEncryptionKey(keyId1, siteId1, now, now, secret1)
166162
}
167163

168164
@Test
169-
public void testRotate_forSiteWithKeyCreatesNewActiveKey(Vertx vertx, VertxTestContext testContext) {
165+
public void testRotate_CreatesOneKeyWhenThereAreMultipleOperatorsPerSite(Vertx vertx, VertxTestContext testContext) {
166+
fakeAuth(Role.MAINTAINER);
167+
168+
setCloudEncryptionKeys();
169+
setOperatorKeys(operator1, operator2);
170+
171+
post(vertx, testContext, Endpoints.CLOUD_ENCRYPTION_KEY_ROTATE, null, rotateResponse -> {
172+
assertEquals(200, rotateResponse.statusCode());
173+
174+
get(vertx, testContext, Endpoints.CLOUD_ENCRYPTION_KEY_LIST, listResponse -> {
175+
assertEquals(200, listResponse.statusCode());
176+
assertEquals(noKeys, parseKeyListResponse(listResponse));
177+
178+
testContext.completeNow();
179+
});
180+
});
181+
}
182+
183+
@Test
184+
public void testRotate_forOperatorSiteWithKeyCreatesNewActiveKey(Vertx vertx, VertxTestContext testContext) {
170185
fakeAuth(Role.MAINTAINER);
171186

172187
var existingKey1 = new CloudEncryptionKey(keyId1, siteId1, before, before, secret1);
@@ -181,7 +196,7 @@ keyId4, new CloudEncryptionKey(4, siteId1, now, now, secret4)
181196
);
182197

183198
setCloudEncryptionKeys(existingKey1, existingKey2, existingKey3);
184-
setSites(site1);
199+
setOperatorKeys(operator1);
185200
when(cloudSecretGenerator.generate()).thenReturn(secret4);
186201
when(clock.getEpochSecond()).thenReturn(now);
187202

@@ -205,7 +220,7 @@ key2Id, new CloudEncryptionKey(key2Id, siteId1, now, now, secret2)
205220
);
206221

207222
setCloudEncryptionKeys(existingKey);
208-
setSites(site1);
223+
setOperatorKeys(operator1);
209224
when(cloudSecretGenerator.generate()).thenReturn(secret2);
210225
when(clock.getEpochSecond()).thenReturn(now);
211226

@@ -220,4 +235,18 @@ private static CloudEncryptionKeyListResponse parseKeyListResponse(HttpResponse<
220235
return OBJECT_MAPPER.readValue(response.bodyAsString(), new TypeReference<>() {
221236
});
222237
}
238+
239+
private static OperatorKey testOperatorKey(int siteId, String keyId) {
240+
return new OperatorKey(
241+
"hash " + keyId,
242+
"key salt " + keyId,
243+
"name " + keyId,
244+
"contact " + keyId,
245+
"protocol " + keyId,
246+
0,
247+
false,
248+
siteId,
249+
keyId
250+
);
251+
}
223252
}

0 commit comments

Comments
 (0)