99import com .uid2 .admin .vertx .RequestUtil ;
1010import com .uid2 .admin .vertx .ResponseUtil ;
1111import com .uid2 .admin .vertx .WriteLock ;
12- import com .uid2 .client .Uid2Helper ;
1312import com .uid2 .shared .audit .AuditParams ;
1413import com .uid2 .shared .auth .Role ;
1514import com .uid2 .shared .model .SaltEntry ;
@@ -77,7 +76,7 @@ public class SaltService implements IService {
7776 private static final String OPERATOR_URL = "http://proxy:80" ;
7877 private static final Map <String , String > OPERATOR_HEADERS = Map .of ("Authorization" , String .format ("Bearer %s" , CLIENT_API_KEY ));
7978
80- private static final int IDENTITY_COUNT = 10_000 ;
79+ private static final int IDENTITY_COUNT = 1_000 ;
8180 private static final int TIMESTAMP_LENGTH = 8 ;
8281 private static final int IV_LENGTH = 12 ;
8382
@@ -110,13 +109,19 @@ public void setupRoutes(Router router) {
110109 }
111110 }, new AuditParams (List .of ("fraction" , "target_date" ), Collections .emptyList ()), Role .SUPER_USER , Role .SECRET_ROTATION ));
112111
113- router .post ("/api/salt/simulate/token" ).blockingHandler (auth .handle ((ctx ) -> {
112+ router .post ("/api/salt/fastForward" ).blockingHandler (auth .handle (ctx -> {
113+ synchronized (writeLock ) {
114+ this .handleSaltFastForward (ctx );
115+ }
116+ }, new AuditParams (List .of ("fraction" ), Collections .emptyList ()), Role .MAINTAINER , Role .SECRET_ROTATION ));
117+
118+ router .post ("/api/salt/simulateToken" ).blockingHandler (auth .handle (ctx -> {
114119 synchronized (writeLock ) {
115120 this .handleSaltSimulateToken (ctx );
116121 }
117- }, new AuditParams (List .of ("target_date" ), Collections .emptyList ()), Role .MAINTAINER , Role .SECRET_ROTATION ));
122+ }, new AuditParams (List .of ("fraction" , " target_date" ), Collections .emptyList ()), Role .MAINTAINER , Role .SECRET_ROTATION ));
118123
119- router .post ("/api/salt/simulate" ).blockingHandler (auth .handle (( ctx ) -> {
124+ router .post ("/api/salt/simulate" ).blockingHandler (auth .handle (ctx -> {
120125 synchronized (writeLock ) {
121126 this .handleSaltSimulate (ctx );
122127 }
@@ -221,26 +226,70 @@ private JsonObject toJson(RotatingSaltProvider.SaltSnapshot snapshot) {
221226 return jo ;
222227 }
223228
229+ private void handleSaltFastForward (RoutingContext rc ) {
230+ try {
231+ final double fraction = RequestUtil .getDouble (rc , "fraction" ).orElse (0.002740 );
232+ final int iterations = RequestUtil .getDouble (rc , "fast_forward_iterations" ).orElse (0.0 ).intValue ();
233+ final boolean enableV4 = RequestUtil .getBoolean (rc , "enable_v4" , false ).orElse (false );
234+
235+ TargetDate targetDate =
236+ RequestUtil .getDate (rc , "target_date" , DateTimeFormatter .ISO_LOCAL_DATE )
237+ .map (TargetDate ::new )
238+ .orElse (TargetDate .now ().plusDays (1 ));
239+
240+ saltProvider .loadContent ();
241+ storageManager .archiveSaltLocations ();
242+
243+ var snapshot = saltProvider .getSnapshots ().getLast ();
244+
245+ saltRotation .setEnableV4RawUid (enableV4 );
246+ var result = saltRotation .rotateSaltsFastForward (snapshot , SALT_ROTATION_AGE_THRESHOLDS , fraction , targetDate , iterations );
247+ if (!result .hasSnapshot ()) {
248+ ResponseUtil .error (rc , 200 , result .getReason ());
249+ return ;
250+ }
251+
252+ storageManager .upload (result .getSnapshot ());
253+
254+ rc .response ()
255+ .putHeader (HttpHeaders .CONTENT_TYPE , "application/json" )
256+ .end (toJson (result .getSnapshot ()).encode ());
257+ } catch (Exception e ) {
258+ LOGGER .error (e .getMessage (), e );
259+ rc .fail (500 , e );
260+ }
261+ }
262+
224263 private void handleSaltSimulateToken (RoutingContext rc ) {
225264 try {
226265 final double fraction = RequestUtil .getDouble (rc , "fraction" ).orElse (0.002740 );
227266 final int iterations = RequestUtil .getDouble (rc , "iterations" ).orElse (0.0 ).intValue ();
267+ final boolean enableV4 = RequestUtil .getBoolean (rc , "enable_v4" , false ).orElse (false );
228268
229269 TargetDate targetDate =
230270 RequestUtil .getDate (rc , "target_date" , DateTimeFormatter .ISO_LOCAL_DATE )
231271 .map (TargetDate ::new )
232272 .orElse (TargetDate .now ().plusDays (1 ));
233273
234- // Step 1. Run /v2/token/generate for 10,000 emails
274+ // Step 1. Run /v2/token/generate for all emails
235275 List <String > emails = new ArrayList <>();
276+ Map <String , Boolean > preRotationEmailToSaltMap = new HashMap <>();
277+ RotatingSaltProvider .SaltSnapshot snapshot = saltProvider .getSnapshots ().getLast ();
236278 for (int j = 0 ; j < IDENTITY_COUNT ; j ++) {
237279 String email = randomEmail ();
238280 emails .add (email );
281+
282+ SaltEntry salt = getSalt (email , snapshot );
283+ boolean isV4 = salt .currentKeySalt () != null && salt .currentKeySalt ().key () != null && salt .currentKeySalt ().salt () != null ;
284+ preRotationEmailToSaltMap .put (email , isV4 );
239285 }
240286
241287 Map <String , String > emailToRefreshTokenMap = new HashMap <>();
242288 Map <String , String > emailToRefreshResponseKeyMap = new HashMap <>();
243- for (String email : emails ) {
289+ for (int i = 0 ; i < emails .size (); i ++) {
290+ LOGGER .info ("Step 1 - Token Generate {}/{}" , i + 1 , emails .size ());
291+ String email = emails .get (i );
292+
244293 JsonNode tokens = v2TokenGenerate (email );
245294 String refreshToken = tokens .at ("/body/refresh_token" ).asText ();
246295 String refreshResponseKey = tokens .at ("/body/refresh_response_key" ).asText ();
@@ -250,23 +299,26 @@ private void handleSaltSimulateToken(RoutingContext rc) {
250299 }
251300
252301 // Step 2. Rotate salts
253- saltRotation .setEnableV4RawUid (true );
254- RotatingSaltProvider .SaltSnapshot snapshot = null ;
302+ saltRotation .setEnableV4RawUid (enableV4 );
255303 for (int i = 0 ; i < iterations ; i ++) {
256- snapshot = rotateSalts (rc , fraction , targetDate , i );
304+ LOGGER .info ("Step 2 - Rotate Salts {}/{}" , i + 1 , iterations );
305+ snapshot = rotateSalts (rc , fraction , targetDate .plusDays (i ), i );
257306 }
258307
259- // Step 3. Count how many emails are v2 vs v4 salts
260- Map <String , Boolean > emailToV4TokenMap = new HashMap <>();
308+ Map <String , Boolean > postRotationEmailToSaltMap = new HashMap <>();
261309 for (String email : emails ) {
262310 SaltEntry salt = getSalt (email , snapshot );
263311 boolean isV4 = salt .currentKeySalt () != null && salt .currentKeySalt ().key () != null && salt .currentKeySalt ().salt () != null ;
264- emailToV4TokenMap .put (email , isV4 );
312+ postRotationEmailToSaltMap .put (email , isV4 );
265313 }
266314
267- // Step 4. Run /v2/token/refresh for all emails
315+ // Step 3. Run /v2/token/refresh for all emails
316+ v2LoadSalts ();
268317 Map <String , Boolean > emailToRefreshSuccessMap = new HashMap <>();
269- for (String email : emails ) {
318+ for (int i = 0 ; i < emails .size (); i ++) {
319+ LOGGER .info ("Step 3 - Token Refresh {}/{}" , i + 1 , emails .size ());
320+ String email = emails .get (i );
321+
270322 try {
271323 JsonNode response = v2TokenRefresh (emailToRefreshTokenMap .get (email ), emailToRefreshResponseKeyMap .get (email ));
272324 emailToRefreshSuccessMap .put (email , response != null );
@@ -277,11 +329,14 @@ private void handleSaltSimulateToken(RoutingContext rc) {
277329 }
278330
279331 LOGGER .info (
280- "UID token simulation: success_count={}, failure_count={}, v4_count={}, v2_count={}" ,
332+ "UID token simulation: success_count={}, failure_count={}, " +
333+ "v2_to_v4_count={}, v4_to_v4_count={}, v4_to_v2_count={}, v2_to_v2_count={}" ,
281334 emailToRefreshSuccessMap .values ().stream ().filter (x -> x ).count (),
282335 emailToRefreshSuccessMap .values ().stream ().filter (x -> !x ).count (),
283- emailToV4TokenMap .values ().stream ().filter (x -> x ).count (),
284- emailToV4TokenMap .values ().stream ().filter (x -> !x ).count ());
336+ emails .stream ().filter (email -> !preRotationEmailToSaltMap .get (email ) && postRotationEmailToSaltMap .get (email )).count (),
337+ emails .stream ().filter (email -> preRotationEmailToSaltMap .get (email ) && postRotationEmailToSaltMap .get (email )).count (),
338+ emails .stream ().filter (email -> preRotationEmailToSaltMap .get (email ) && !postRotationEmailToSaltMap .get (email )).count (),
339+ emails .stream ().filter (email -> !preRotationEmailToSaltMap .get (email ) && !postRotationEmailToSaltMap .get (email )).count ());
285340
286341 rc .response ()
287342 .putHeader (HttpHeaders .CONTENT_TYPE , "application/json" )
@@ -296,9 +351,9 @@ private void handleSaltSimulate(RoutingContext rc) {
296351 try {
297352 final double fraction = RequestUtil .getDouble (rc , "fraction" ).orElse (0.002740 );
298353
299- final int preMigrationIterations = RequestUtil .getDouble (rc , "preMigrationIterations " ).orElse (0.0 ).intValue ();
300- final int migrationV4Iterations = RequestUtil .getDouble (rc , "migrationV4Iterations " ).orElse (0.0 ).intValue ();
301- final int migrationV2V3Iterations = RequestUtil .getDouble (rc , "migrationV2V3Iterations " ).orElse (0.0 ).intValue ();
354+ final int preMigrationIterations = RequestUtil .getDouble (rc , "pre_migration_iterations " ).orElse (0.0 ).intValue ();
355+ final int migrationV4Iterations = RequestUtil .getDouble (rc , "migration_v4_iterations " ).orElse (0.0 ).intValue ();
356+ final int migrationV2Iterations = RequestUtil .getDouble (rc , "migration_v2_iterations " ).orElse (0.0 ).intValue ();
302357
303358 TargetDate targetDate =
304359 RequestUtil .getDate (rc , "target_date" , DateTimeFormatter .ISO_LOCAL_DATE )
@@ -331,8 +386,8 @@ private void handleSaltSimulate(RoutingContext rc) {
331386 }
332387
333388 saltRotation .setEnableV4RawUid (false );
334- for (int i = 0 ; i < migrationV2V3Iterations ; i ++) {
335- LOGGER .info ("Step 3 - Migration V2/V3 Iteration {}/{}" , i + 1 , migrationV2V3Iterations );
389+ for (int i = 0 ; i < migrationV2Iterations ; i ++) {
390+ LOGGER .info ("Step 3 - Migration V2 Iteration {}/{}" , i + 1 , migrationV2Iterations );
336391 simulationIteration (rc , fraction , targetDate , preMigrationIterations + migrationV4Iterations + i , false , emails , emailToUidMapping );
337392 targetDate = targetDate .plusDays (1 );
338393 }
@@ -723,6 +778,10 @@ private String toBase64String(byte[] b) {
723778 return Base64 .getEncoder ().encodeToString (b );
724779 }
725780
781+ private void v2LoadSalts () throws Exception {
782+ HTTP_CLIENT .post (String .format ("%s/v2/salts/load" , OPERATOR_URL ), "" , OPERATOR_HEADERS );
783+ }
784+
726785 private JsonNode v3IdentityMap (List <String > emails ) throws Exception {
727786 StringBuilder reqBody = new StringBuilder ("{ \" email\" : [" );
728787 for (String email : emails ) {
0 commit comments