@@ -140,6 +140,12 @@ public void setupRoutes(Router router) {
140140 }
141141 }, new AuditParams (List .of (), Collections .emptyList ()), Role .MAINTAINER , Role .SECRET_ROTATION ));
142142
143+ router .post ("/api/salt/rollout" ).blockingHandler (auth .handle (ctx -> {
144+ synchronized (writeLock ) {
145+ this .handleSaltRollout (ctx );
146+ }
147+ }, new AuditParams (List .of (), Collections .emptyList ()), Role .MAINTAINER , Role .SECRET_ROTATION ));
148+
143149 router .post ("/api/salt/simulateToken" ).blockingHandler (auth .handle (ctx -> {
144150 synchronized (writeLock ) {
145151 this .handleSaltSimulateToken (ctx );
@@ -474,6 +480,35 @@ private void handleSaltOptout(RoutingContext rc) {
474480 }
475481 }
476482
483+ private void handleSaltRollout (RoutingContext rc ) {
484+ try {
485+ final String candidateOperatorUrl = RequestUtil .getString (rc , "candidate_operator_url" ).orElse ("" );
486+ final String candidateApiKey = RequestUtil .getString (rc , "candidate_api_key" ).orElse ("" );
487+ final String candidateApiSecret = RequestUtil .getString (rc , "candidate_api_secret" ).orElse ("" );
488+
489+ RotatingSaltProvider .SaltSnapshot snapshot = saltProvider .getSnapshots ().getLast ();
490+
491+ List <String > emails = new ArrayList <>();
492+ while (emails .size () < 10_000 ) {
493+ String email = randomEmail ();
494+ SaltEntry salt = getSalt (email , snapshot );
495+
496+ if (salt .currentSalt () == null ) {
497+ emails .add (email );
498+ }
499+ }
500+
501+ JsonNode response = v3IdentityMap (emails , candidateOperatorUrl , candidateApiKey , candidateApiSecret );
502+
503+ rc .response ()
504+ .putHeader (HttpHeaders .CONTENT_TYPE , "application/json" )
505+ .end ();
506+ } catch (Exception e ) {
507+ LOGGER .error (e .getMessage (), e );
508+ rc .fail (500 , e );
509+ }
510+ }
511+
477512 private void handleSaltSimulateToken (RoutingContext rc ) {
478513 try {
479514 final double fraction = RequestUtil .getDouble (rc , "fraction" ).orElse (0.002740 );
0 commit comments