Skip to content

Commit 86b47c9

Browse files
added feature switch for default min ages in salt rotation
1 parent 12e988a commit 86b47c9

File tree

4 files changed

+96
-8
lines changed

4 files changed

+96
-8
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ public class AdminConst {
66
public static final String ROLE_OKTA_GROUP_MAP_PRIVILEGED = "role_okta_group_map_privileged";
77
public static final String ROLE_OKTA_GROUP_MAP_SUPER_USER = "role_okta_group_map_super_user";
88
public static final String ENABLE_SALT_ROTATION_REFRESH_FROM = "enable_salt_rotation_refresh_from";
9+
public static final String ENABLE_SALT_ROTATION_CUSTOM_AGE_THRESHOLDS = "enable_salt_rotation_custom_age_thresholds";
910
}

src/main/java/com/uid2/admin/salt/SaltRotation.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,17 @@ public class SaltRotation {
2323

2424
private final IKeyGenerator keyGenerator;
2525
private final boolean isRefreshFromEnabled;
26+
private final boolean isCustomAgeThresholdEnabled;
2627
private static final Logger LOGGER = LoggerFactory.getLogger(SaltRotation.class);
2728

2829
public SaltRotation(JsonObject config, IKeyGenerator keyGenerator) {
2930
this.keyGenerator = keyGenerator;
3031
this.isRefreshFromEnabled = config.getBoolean(AdminConst.ENABLE_SALT_ROTATION_REFRESH_FROM, false);
32+
this.isCustomAgeThresholdEnabled = config.getBoolean(AdminConst.ENABLE_SALT_ROTATION_CUSTOM_AGE_THRESHOLDS, false);
33+
}
34+
35+
public boolean isCustomAgeThresholdEnabled() {
36+
return this.isCustomAgeThresholdEnabled;
3137
}
3238

3339
public Result rotateSalts(

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

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.uid2.admin.vertx.service;
22

3+
import com.uid2.admin.AdminConst;
34
import com.uid2.admin.auth.AdminAuthMiddleware;
45
import com.uid2.admin.salt.SaltRotation;
56
import com.uid2.admin.salt.TargetDate;
@@ -21,10 +22,7 @@
2122

2223
import java.time.*;
2324
import java.time.format.DateTimeFormatter;
24-
import java.util.Arrays;
25-
import java.util.Collections;
26-
import java.util.List;
27-
import java.util.Optional;
25+
import java.util.*;
2826

2927
import static com.uid2.admin.vertx.Endpoints.*;
3028

@@ -36,6 +34,7 @@ public class SaltService implements IService {
3634
private final SaltStoreWriter storageManager;
3735
private final RotatingSaltProvider saltProvider;
3836
private final SaltRotation saltRotation;
37+
private final Duration[] defaultSaltRotationAgeThresholds;
3938

4039
public SaltService(AdminAuthMiddleware auth,
4140
WriteLock writeLock,
@@ -47,6 +46,7 @@ public SaltService(AdminAuthMiddleware auth,
4746
this.storageManager = storageManager;
4847
this.saltProvider = saltProvider;
4948
this.saltRotation = saltRotation;
49+
this.defaultSaltRotationAgeThresholds = generateThresholds(30, 390, 30);
5050
}
5151

5252
@Override
@@ -117,8 +117,15 @@ private void handleSaltRotate(RoutingContext rc) {
117117
try {
118118
final Optional<Double> fraction = RequestUtil.getDouble(rc, "fraction");
119119
if (fraction.isEmpty()) return;
120-
final Duration[] minAges = RequestUtil.getDurations(rc, "min_ages_in_seconds");
121-
if (minAges == null) return;
120+
121+
final Duration[] ageThresholds;
122+
if (saltRotation.isCustomAgeThresholdEnabled()) {
123+
ageThresholds = RequestUtil.getDurations(rc, "min_ages_in_seconds");
124+
if (ageThresholds == null) return;
125+
} else {
126+
ageThresholds = defaultSaltRotationAgeThresholds;
127+
}
128+
122129

123130
final TargetDate targetDate =
124131
RequestUtil.getDate(rc, "target_date", DateTimeFormatter.ISO_LOCAL_DATE)
@@ -134,7 +141,7 @@ private void handleSaltRotate(RoutingContext rc) {
134141
final List<RotatingSaltProvider.SaltSnapshot> snapshots = saltProvider.getSnapshots();
135142
final RotatingSaltProvider.SaltSnapshot lastSnapshot = snapshots.getLast();
136143

137-
final SaltRotation.Result result = saltRotation.rotateSalts(lastSnapshot, minAges, fraction.get(), targetDate);
144+
final SaltRotation.Result result = saltRotation.rotateSalts(lastSnapshot, ageThresholds, fraction.get(), targetDate);
138145
if (!result.hasSnapshot()) {
139146
ResponseUtil.error(rc, 200, result.getReason());
140147
return;
@@ -151,6 +158,14 @@ private void handleSaltRotate(RoutingContext rc) {
151158
}
152159
}
153160

161+
private Duration[] generateThresholds(int minAge, int maxAge, int interval) {
162+
List<Duration> thresholds = new ArrayList<>();
163+
for (int i = minAge; i <= maxAge; i += interval) {
164+
thresholds.add(Duration.ofDays(i));
165+
}
166+
return thresholds.toArray(new Duration[0]);
167+
}
168+
154169
private JsonObject toJson(RotatingSaltProvider.SaltSnapshot snapshot) {
155170
JsonObject jo = new JsonObject();
156171
jo.put("effective", snapshot.getEffective().toEpochMilli());

src/test/java/com/uid2/admin/salt/SaltServiceTest.java

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import io.vertx.junit5.VertxTestContext;
1212
import org.junit.jupiter.api.Test;
1313
import org.mockito.Mock;
14-
14+
import java.time.Duration;
1515
import java.time.Instant;
1616
import java.util.Arrays;
1717

@@ -133,6 +133,72 @@ void rotateSaltsWitnSpecificTargetDate(Vertx vertx, VertxTestContext testContext
133133
});
134134
}
135135

136+
@Test
137+
void rotateSaltsWithCustomAgeThresholdsEnabled(Vertx vertx, VertxTestContext testContext) throws Exception {
138+
fakeAuth(Role.SUPER_USER);
139+
140+
when(saltRotation.isCustomAgeThresholdEnabled()).thenReturn(true);
141+
142+
final SaltSnapshotBuilder lastSnapshot = SaltSnapshotBuilder.start().effective(daysEarlier(1)).expires(daysLater(6)).entries(1, daysEarlier(1));
143+
setSnapshots(lastSnapshot);
144+
145+
var result = SaltRotation.Result.fromSnapshot(SaltSnapshotBuilder.start().effective(targetDate()).expires(daysEarlier(7)).entries(1, targetDate()).build());
146+
147+
Duration[] expectedCustomAgeThresholds = new Duration[]{
148+
Duration.ofSeconds(50),
149+
Duration.ofSeconds(60),
150+
Duration.ofSeconds(70)
151+
};
152+
153+
when(saltRotation.rotateSalts(any(), eq(expectedCustomAgeThresholds), eq(0.2), eq(utcTomorrow))).thenReturn(result);
154+
155+
post(vertx, testContext, "api/salt/rotate?min_ages_in_seconds=50,60,70&fraction=0.2", "", response -> {
156+
assertEquals(200, response.statusCode());
157+
testContext.completeNow();
158+
});
159+
}
160+
161+
@Test
162+
void rotateSaltsWithCustomAgeThresholdsDisabled(Vertx vertx, VertxTestContext testContext) throws Exception {
163+
fakeAuth(Role.SUPER_USER);
164+
165+
when(saltRotation.isCustomAgeThresholdEnabled()).thenReturn(false);
166+
167+
final SaltSnapshotBuilder lastSnapshot = SaltSnapshotBuilder.start().effective(daysEarlier(1)).expires(daysLater(6)).entries(1, daysEarlier(1));
168+
setSnapshots(lastSnapshot);
169+
170+
var result = SaltRotation.Result.fromSnapshot(SaltSnapshotBuilder.start().effective(targetDate()).expires(daysEarlier(7)).entries(1, targetDate()).build());
171+
172+
Duration[] expectedDefaultAgeThresholds = new Duration[]{
173+
Duration.ofDays(30), Duration.ofDays(60), Duration.ofDays(90), Duration.ofDays(120),
174+
Duration.ofDays(150), Duration.ofDays(180), Duration.ofDays(210), Duration.ofDays(240),
175+
Duration.ofDays(270), Duration.ofDays(300), Duration.ofDays(330), Duration.ofDays(360),
176+
Duration.ofDays(390)
177+
};
178+
179+
when(saltRotation.rotateSalts(any(), eq(expectedDefaultAgeThresholds), eq(0.2), eq(utcTomorrow))).thenReturn(result);
180+
181+
post(vertx, testContext, "api/salt/rotate?min_ages_in_seconds=50,60,70&fraction=0.2", "", response -> {
182+
assertEquals(200, response.statusCode());
183+
testContext.completeNow();
184+
});
185+
}
186+
187+
@Test
188+
void rotateSaltsWithCustomAgeThresholdsEnabledButMissingParameter(Vertx vertx, VertxTestContext testContext) throws Exception {
189+
fakeAuth(Role.SUPER_USER);
190+
191+
when(saltRotation.isCustomAgeThresholdEnabled()).thenReturn(true);
192+
193+
final SaltSnapshotBuilder lastSnapshot = SaltSnapshotBuilder.start().effective(daysEarlier(1)).expires(daysLater(6)).entries(1, daysEarlier(1));
194+
setSnapshots(lastSnapshot);
195+
196+
post(vertx, testContext, "api/salt/rotate?fraction=0.2", "", response -> {
197+
assertEquals(400, response.statusCode());
198+
testContext.completeNow();
199+
});
200+
}
201+
136202
private void checkSnapshotsResponse(SaltSnapshotBuilder[] expectedSnapshots, Object[] actualSnapshots) {
137203
assertEquals(expectedSnapshots.length, actualSnapshots.length);
138204
for (int i = 0; i < expectedSnapshots.length; ++i) {

0 commit comments

Comments
 (0)