11package com .uid2 .admin .cloudencryption ;
22
3+ import com .uid2 .admin .model .CloudEncryptionKeySummary ;
34import com .uid2 .admin .store .writer .CloudEncryptionKeyStoreWriter ;
45import com .uid2 .shared .auth .OperatorKey ;
6+ import com .uid2 .shared .auth .RotatingOperatorKeyProvider ;
57import com .uid2 .shared .model .CloudEncryptionKey ;
68import com .uid2 .shared .store .reader .RotatingCloudEncryptionKeyProvider ;
79import org .slf4j .Logger ;
810import org .slf4j .LoggerFactory ;
911
10- import java .time .Instant ;
1112import java .util .*;
13+ import java .util .function .Function ;
14+ import java .util .stream .Collectors ;
1215
1316public class CloudEncryptionKeyManager {
14-
15- private static final Logger LOGGER = LoggerFactory . getLogger ( CloudEncryptionKeyManager . class ) ;
16-
17- private final RotatingCloudEncryptionKeyProvider RotatingCloudEncryptionKeyProvider ;
18- private final CloudEncryptionKeyStoreWriter cloudEncryptionKeyStoreWriter ;
19- private final CloudSecretGenerator secretGenerator ;
17+ private final RotatingCloudEncryptionKeyProvider keyProvider ;
18+ private final RotatingOperatorKeyProvider operatorKeyProvider ;
19+ private final CloudEncryptionKeyStoreWriter keyWriter ;
20+ private final CloudKeyStatePlanner planner ;
21+ private Collection < OperatorKey > operatorKeys ;
22+ private Collection < CloudEncryptionKey > existingKeys ;
2023
2124 public CloudEncryptionKeyManager (
22- RotatingCloudEncryptionKeyProvider RotatingCloudEncryptionKeyProvider ,
23- CloudEncryptionKeyStoreWriter cloudEncryptionKeyStoreWriter ,
24- CloudSecretGenerator keyGenerator
25- ) {
26- this .RotatingCloudEncryptionKeyProvider = RotatingCloudEncryptionKeyProvider ;
27- this .cloudEncryptionKeyStoreWriter = cloudEncryptionKeyStoreWriter ;
28- this .secretGenerator = keyGenerator ;
29- }
30-
31- // Ensures there are `keyCountPerSite` sites for each site corresponding of operatorKeys. If there are less - create new ones.
32- // Give all new keys for each site `activationInterval` seconds between activations, starting now
33- public void generateKeysForOperators (
34- Collection <OperatorKey > operatorKeys ,
35- long activationInterval ,
36- int keyCountPerSite
37- ) throws Exception {
38- this .RotatingCloudEncryptionKeyProvider .loadContent ();
39-
40- if (operatorKeys == null || operatorKeys .isEmpty ()) {
41- throw new IllegalArgumentException ("Operator keys collection must not be null or empty" );
42- }
43- if (activationInterval <= 0 ) {
44- throw new IllegalArgumentException ("Key activate interval must be greater than zero" );
45- }
46- if (keyCountPerSite <= 0 ) {
47- throw new IllegalArgumentException ("Key count per site must be greater than zero" );
48- }
49-
50- for (Integer siteId : uniqueSiteIdsForOperators (operatorKeys )) {
51- ensureEnoughKeysForSite (activationInterval , keyCountPerSite , siteId );
52- }
25+ RotatingCloudEncryptionKeyProvider keyProvider ,
26+ CloudEncryptionKeyStoreWriter keyWriter ,
27+ RotatingOperatorKeyProvider operatorKeyProvider ,
28+ CloudKeyStatePlanner planner ) {
29+ this .keyProvider = keyProvider ;
30+ this .operatorKeyProvider = operatorKeyProvider ;
31+ this .keyWriter = keyWriter ;
32+ this .planner = planner ;
5333 }
5434
55- private void ensureEnoughKeysForSite (long activationInterval , int keyCountPerSite , Integer siteId ) throws Exception {
56- // Check if the site ID already exists in the S3 key provider and has fewer than the required number of keys
57- int currentKeyCount = countKeysForSite (siteId );
58- if (currentKeyCount >= keyCountPerSite ) {
59- LOGGER .info ("Site ID {} already has the required number of keys. Skipping key generation." , siteId );
60- return ;
61- }
62-
63- int keysToGenerate = keyCountPerSite - currentKeyCount ;
64- for (int i = 0 ; i < keysToGenerate ; i ++) {
65- addKey (activationInterval , siteId , i );
66- }
67- LOGGER .info ("Generated {} keys for site ID {}" , keysToGenerate , siteId );
68- }
69-
70- private void addKey (long keyActivateInterval , Integer siteId , int keyIndex ) throws Exception {
71- long created = Instant .now ().getEpochSecond ();
72- long activated = created + (keyIndex * keyActivateInterval );
73- CloudEncryptionKey cloudEncryptionKey = generateCloudEncryptionKey (siteId , activated , created );
74- addCloudEncryptionKey (cloudEncryptionKey );
75- }
35+ private static final Logger logger = LoggerFactory .getLogger (CloudEncryptionKeyManager .class );
7636
77- private static Set < Integer > uniqueSiteIdsForOperators ( Collection < OperatorKey > operatorKeys ) {
78- Set < Integer > uniqueSiteIds = new HashSet <>();
79- for ( OperatorKey operatorKey : operatorKeys ) {
80- uniqueSiteIds . add ( operatorKey . getSiteId () );
81- }
82- return uniqueSiteIds ;
37+ // For any site that has an operator create a new key activating now
38+ // Keep up to 5 most recent old keys per site, delete the rest
39+ public void rotateKeys () throws Exception {
40+ refreshCloudData ( );
41+ var desiredKeys = planner . planRotation ( existingKeys , operatorKeys );
42+ writeKeys ( desiredKeys ) ;
8343 }
8444
85- CloudEncryptionKey generateCloudEncryptionKey (int siteId , long activates , long created ) throws Exception {
86- int newKeyId = getNextKeyId ();
87- String secret = secretGenerator .generate ();
88- return new CloudEncryptionKey (newKeyId , siteId , activates , created , secret );
45+ // For any site that has an operator, if there are no keys, create a key activating now
46+ public void backfillKeys () throws Exception {
47+ refreshCloudData ();
48+ var desiredKeys = planner .planBackfill (existingKeys , operatorKeys );
49+ writeKeys (desiredKeys );
8950 }
9051
91- void addCloudEncryptionKey (CloudEncryptionKey cloudEncryptionKey ) throws Exception {
92- Map <Integer , CloudEncryptionKey > cloudEncryptionKeys = new HashMap <>(RotatingCloudEncryptionKeyProvider .getAll ());
93- cloudEncryptionKeys .put (cloudEncryptionKey .getId (), cloudEncryptionKey );
94- cloudEncryptionKeyStoreWriter .upload (cloudEncryptionKeys , null );
52+ public List <CloudEncryptionKeySummary > getKeySummaries () throws Exception {
53+ refreshCloudData ();
54+ return existingKeys .stream ().map (CloudEncryptionKeySummary ::fromFullKey ).toList ();
9555 }
9656
97- int getNextKeyId () {
98- Map < Integer , CloudEncryptionKey > cloudEncryptionKeys = RotatingCloudEncryptionKeyProvider . getAll ();
99- if ( cloudEncryptionKeys == null || cloudEncryptionKeys . isEmpty ()) {
100- return 1 ;
101- }
102- return cloudEncryptionKeys . keySet (). stream (). max ( Integer :: compareTo ). orElse ( 0 ) + 1 ;
57+ private void writeKeys ( Set < CloudEncryptionKey > desiredKeys ) throws Exception {
58+ var keysForWriting = desiredKeys . stream (). collect ( Collectors . toMap (
59+ CloudEncryptionKey :: getId ,
60+ Function . identity ())
61+ );
62+ keyWriter . upload ( keysForWriting , null ) ;
10363 }
10464
105- int countKeysForSite (int siteId ) {
106- Map <Integer , CloudEncryptionKey > allKeys = RotatingCloudEncryptionKeyProvider .getAll ();
107- return (int ) allKeys .values ().stream ().filter (key -> key .getSiteId () == siteId ).count ();
65+ private void refreshCloudData () throws Exception {
66+ keyProvider .loadContent ();
67+ operatorKeyProvider .loadContent (operatorKeyProvider .getMetadata ());
68+ operatorKeys = operatorKeyProvider .getAll ();
69+ existingKeys = keyProvider .getAll ().values ();
10870 }
10971}
0 commit comments