Skip to content

Commit 3d9dbe2

Browse files
Merge pull request #549 from IABTechLab/sch-UID2-5444-hardcode-salt-rotation-min-ages
sch-UID2-5444 added feature switch for default min ages in salt rotation
2 parents 12e988a + 5fb8972 commit 3d9dbe2

File tree

5 files changed

+107
-10
lines changed

5 files changed

+107
-10
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>com.uid2</groupId>
88
<artifactId>uid2-admin</artifactId>
9-
<version>6.9.0</version>
9+
<version>6.9.1-alpha-202-SNAPSHOT</version>
1010

1111
<properties>
1212
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

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: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,22 +21,36 @@
2121

2222
import java.time.*;
2323
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;
24+
import java.util.*;
25+
import java.util.stream.Collectors;
2826

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

3129
public class SaltService implements IService {
3230
private static final Logger LOGGER = LoggerFactory.getLogger(SaltService.class);
31+
private static final Duration[] SALT_ROTATION_AGE_THRESHOLDS = new Duration[]{
32+
Duration.ofDays(30),
33+
Duration.ofDays(60),
34+
Duration.ofDays(90),
35+
Duration.ofDays(120),
36+
Duration.ofDays(150),
37+
Duration.ofDays(180),
38+
Duration.ofDays(210),
39+
Duration.ofDays(240),
40+
Duration.ofDays(270),
41+
Duration.ofDays(300),
42+
Duration.ofDays(330),
43+
Duration.ofDays(360),
44+
Duration.ofDays(390)
45+
};
3346

3447
private final AdminAuthMiddleware auth;
3548
private final WriteLock writeLock;
3649
private final SaltStoreWriter storageManager;
3750
private final RotatingSaltProvider saltProvider;
3851
private final SaltRotation saltRotation;
3952

53+
4054
public SaltService(AdminAuthMiddleware auth,
4155
WriteLock writeLock,
4256
SaltStoreWriter storageManager,
@@ -117,8 +131,15 @@ private void handleSaltRotate(RoutingContext rc) {
117131
try {
118132
final Optional<Double> fraction = RequestUtil.getDouble(rc, "fraction");
119133
if (fraction.isEmpty()) return;
120-
final Duration[] minAges = RequestUtil.getDurations(rc, "min_ages_in_seconds");
121-
if (minAges == null) return;
134+
135+
final Duration[] ageThresholds;
136+
if (saltRotation.isCustomAgeThresholdEnabled()) {
137+
ageThresholds = RequestUtil.getDurations(rc, "min_ages_in_seconds");
138+
if (ageThresholds == null) return;
139+
} else {
140+
ageThresholds = SALT_ROTATION_AGE_THRESHOLDS;
141+
}
142+
LOGGER.info("Salt rotation age thresholds in seconds: {}", Arrays.stream(ageThresholds).map(Duration::toSeconds).collect(Collectors.toList()));
122143

123144
final TargetDate targetDate =
124145
RequestUtil.getDate(rc, "target_date", DateTimeFormatter.ISO_LOCAL_DATE)
@@ -134,7 +155,7 @@ private void handleSaltRotate(RoutingContext rc) {
134155
final List<RotatingSaltProvider.SaltSnapshot> snapshots = saltProvider.getSnapshots();
135156
final RotatingSaltProvider.SaltSnapshot lastSnapshot = snapshots.getLast();
136157

137-
final SaltRotation.Result result = saltRotation.rotateSalts(lastSnapshot, minAges, fraction.get(), targetDate);
158+
final SaltRotation.Result result = saltRotation.rotateSalts(lastSnapshot, ageThresholds, fraction.get(), targetDate);
138159
if (!result.hasSnapshot()) {
139160
ResponseUtil.error(rc, 200, result.getReason());
140161
return;

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

Lines changed: 71 additions & 2 deletions
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

@@ -111,7 +111,7 @@ void rotateSaltsNoNewSnapshot(Vertx vertx, VertxTestContext testContext) throws
111111
}
112112

113113
@Test
114-
void rotateSaltsWitnSpecificTargetDate(Vertx vertx, VertxTestContext testContext) throws Exception {
114+
void rotateSaltsWithSpecificTargetDate(Vertx vertx, VertxTestContext testContext) throws Exception {
115115
fakeAuth(Role.SUPER_USER);
116116
final SaltSnapshotBuilder[] snapshots = {
117117
SaltSnapshotBuilder.start().effective(daysEarlier(5)).expires(daysEarlier(4)).entries(10, daysEarlier(5)),
@@ -133,6 +133,75 @@ 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+
verify(saltRotation).rotateSalts(any(), eq(expectedCustomAgeThresholds), eq(0.2), eq(utcTomorrow));
157+
assertEquals(200, response.statusCode());
158+
testContext.completeNow();
159+
});
160+
}
161+
162+
@Test
163+
void rotateSaltsWithDefaultAgeThresholds(Vertx vertx, VertxTestContext testContext) throws Exception {
164+
fakeAuth(Role.SUPER_USER);
165+
166+
when(saltRotation.isCustomAgeThresholdEnabled()).thenReturn(false);
167+
168+
final SaltSnapshotBuilder lastSnapshot = SaltSnapshotBuilder.start().effective(daysEarlier(1)).expires(daysLater(6)).entries(1, daysEarlier(1));
169+
setSnapshots(lastSnapshot);
170+
171+
var result = SaltRotation.Result.fromSnapshot(SaltSnapshotBuilder.start().effective(targetDate()).expires(daysEarlier(7)).entries(1, targetDate()).build());
172+
173+
Duration[] expectedDefaultAgeThresholds = new Duration[]{
174+
Duration.ofDays(30), Duration.ofDays(60), Duration.ofDays(90), Duration.ofDays(120),
175+
Duration.ofDays(150), Duration.ofDays(180), Duration.ofDays(210), Duration.ofDays(240),
176+
Duration.ofDays(270), Duration.ofDays(300), Duration.ofDays(330), Duration.ofDays(360),
177+
Duration.ofDays(390)
178+
};
179+
180+
when(saltRotation.rotateSalts(any(), eq(expectedDefaultAgeThresholds), eq(0.2), eq(utcTomorrow))).thenReturn(result);
181+
182+
post(vertx, testContext, "api/salt/rotate?min_ages_in_seconds=50,60,70&fraction=0.2", "", response -> {
183+
verify(saltRotation).rotateSalts(any(), eq(expectedDefaultAgeThresholds), eq(0.2), eq(utcTomorrow));
184+
assertEquals(200, response.statusCode());
185+
testContext.completeNow();
186+
});
187+
}
188+
189+
@Test
190+
void rotateSaltsWithCustomAgeThresholdsEnabledButMissingParameter(Vertx vertx, VertxTestContext testContext) {
191+
fakeAuth(Role.SUPER_USER);
192+
193+
when(saltRotation.isCustomAgeThresholdEnabled()).thenReturn(true);
194+
195+
final SaltSnapshotBuilder lastSnapshot = SaltSnapshotBuilder.start().effective(daysEarlier(1)).expires(daysLater(6)).entries(1, daysEarlier(1));
196+
setSnapshots(lastSnapshot);
197+
198+
post(vertx, testContext, "api/salt/rotate?fraction=0.2", "", response -> {
199+
verify(saltRotation, never()).rotateSalts(any(), any(), anyDouble(), any());
200+
assertEquals(400, response.statusCode());
201+
testContext.completeNow();
202+
});
203+
}
204+
136205
private void checkSnapshotsResponse(SaltSnapshotBuilder[] expectedSnapshots, Object[] actualSnapshots) {
137206
assertEquals(expectedSnapshots.length, actualSnapshots.length);
138207
for (int i = 0; i < expectedSnapshots.length; ++i) {

0 commit comments

Comments
 (0)