diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java index bc0e83ce6..35917a53d 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/VerificationController.java @@ -229,9 +229,16 @@ public class VerificationController { throw new ServerErrorException(Response.Status.INTERNAL_SERVER_ERROR, e); } - VerificationSession verificationSession = new VerificationSession(null, new ArrayList<>(), - Collections.emptyList(), null, null, false, - clock.millis(), clock.millis(), registrationServiceSession.expiration()); + VerificationSession verificationSession = new VerificationSession(null, + maybeCarrierData.orElse(null), + new ArrayList<>(), + Collections.emptyList(), + null, + null, + false, + clock.millis(), + clock.millis(), + registrationServiceSession.expiration()); verificationSession = handlePushToken(pushTokenAndType, verificationSession); // unconditionally request a captcha -- it will either be the only requested information, or a fallback @@ -347,10 +354,16 @@ public class VerificationController { requestedInformation.add(VerificationSession.Information.PUSH_CHALLENGE); requestedInformation.addAll(verificationSession.requestedInformation()); - verificationSession = new VerificationSession(generatePushChallenge(), requestedInformation, - verificationSession.submittedInformation(), verificationSession.smsSenderOverride(), - verificationSession.voiceSenderOverride(), verificationSession.allowedToRequestCode(), - verificationSession.createdTimestamp(), clock.millis(), verificationSession.remoteExpirationSeconds() + verificationSession = new VerificationSession(generatePushChallenge(), + verificationSession.carrierData(), + requestedInformation, + verificationSession.submittedInformation(), + verificationSession.smsSenderOverride(), + verificationSession.voiceSenderOverride(), + verificationSession.allowedToRequestCode(), + verificationSession.createdTimestamp(), + clock.millis(), + verificationSession.remoteExpirationSeconds() ); } @@ -415,9 +428,15 @@ public class VerificationController { || requestedInformation.remove(VerificationSession.Information.PUSH_CHALLENGE)) && requestedInformation.isEmpty(); - verificationSession = new VerificationSession(verificationSession.pushChallenge(), requestedInformation, - submittedInformation, verificationSession.smsSenderOverride(), verificationSession.voiceSenderOverride(), - allowedToRequestCode, verificationSession.createdTimestamp(), clock.millis(), + verificationSession = new VerificationSession(verificationSession.pushChallenge(), + verificationSession.carrierData(), + requestedInformation, + submittedInformation, + verificationSession.smsSenderOverride(), + verificationSession.voiceSenderOverride(), + allowedToRequestCode, + verificationSession.createdTimestamp(), + clock.millis(), verificationSession.remoteExpirationSeconds()); } else if (pushChallengePresent) { @@ -482,9 +501,15 @@ public class VerificationController { || requestedInformation.remove(VerificationSession.Information.CAPTCHA)) && requestedInformation.isEmpty(); - verificationSession = new VerificationSession(verificationSession.pushChallenge(), requestedInformation, - submittedInformation, verificationSession.smsSenderOverride(), verificationSession.voiceSenderOverride(), - allowedToRequestCode, verificationSession.createdTimestamp(), clock.millis(), + verificationSession = new VerificationSession(verificationSession.pushChallenge(), + verificationSession.carrierData(), + requestedInformation, + submittedInformation, + verificationSession.smsSenderOverride(), + verificationSession.voiceSenderOverride(), + allowedToRequestCode, + verificationSession.createdTimestamp(), + clock.millis(), verificationSession.remoteExpirationSeconds()); } else { throw new ForbiddenException(); diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/registration/VerificationSession.java b/service/src/main/java/org/whispersystems/textsecuregcm/registration/VerificationSession.java index b1514583e..398ee2f73 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/registration/VerificationSession.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/registration/VerificationSession.java @@ -7,10 +7,10 @@ package org.whispersystems.textsecuregcm.registration; import com.fasterxml.jackson.annotation.JsonProperty; import java.time.Instant; -import java.util.ArrayList; import java.util.List; import javax.annotation.Nullable; import org.whispersystems.textsecuregcm.storage.SerializedExpireableJsonDynamoStore; +import org.whispersystems.textsecuregcm.telephony.CarrierData; /** * Server-internal stored session object. Primarily used by @@ -19,6 +19,7 @@ import org.whispersystems.textsecuregcm.storage.SerializedExpireableJsonDynamoSt * {@link org.whispersystems.textsecuregcm.controllers.RegistrationController}. * * @param pushChallenge the value of a push challenge sent to a client, after it submitted a push token + * @param carrierData information about the phone number's carrier if available * @param requestedInformation information requested that a client send to the server * @param submittedInformation information that a client has submitted and that the server has verified * @param smsSenderOverride if present, indicates a sender override argument that should be forwarded to the @@ -36,6 +37,7 @@ import org.whispersystems.textsecuregcm.storage.SerializedExpireableJsonDynamoSt */ public record VerificationSession( @Nullable String pushChallenge, + @Nullable CarrierData carrierData, List requestedInformation, List submittedInformation, @Nullable String smsSenderOverride, diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/VerificationControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/VerificationControllerTest.java index 546133ec5..e49be1980 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/VerificationControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/VerificationControllerTest.java @@ -393,7 +393,7 @@ class VerificationControllerTest { when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( Optional.of( - new VerificationSession(null, List.of(VerificationSession.Information.CAPTCHA), Collections.emptyList(), + new VerificationSession(null, null, List.of(VerificationSession.Information.CAPTCHA), Collections.emptyList(), null, null, false, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); when(verificationSessionManager.update(any(), any())) @@ -437,7 +437,7 @@ class VerificationControllerTest { registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, Collections.emptyList(), Collections.emptyList(), null, null, false, + Optional.of(new VerificationSession(null, null, Collections.emptyList(), Collections.emptyList(), null, null, false, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); when(verificationSessionManager.update(any(), any())) @@ -473,7 +473,7 @@ class VerificationControllerTest { registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, Collections.emptyList(), Collections.emptyList(), null, null, false, + Optional.of(new VerificationSession(null, null, Collections.emptyList(), Collections.emptyList(), null, null, false, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); when(verificationSessionManager.update(any(), any())) @@ -509,7 +509,7 @@ class VerificationControllerTest { registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession("challenge", List.of(VerificationSession.Information.PUSH_CHALLENGE), + Optional.of(new VerificationSession("challenge", null, List.of(VerificationSession.Information.PUSH_CHALLENGE), Collections.emptyList(), null, null, false, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); when(verificationSessionManager.update(any(), any())) @@ -543,7 +543,7 @@ class VerificationControllerTest { registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, List.of(VerificationSession.Information.CAPTCHA), + Optional.of(new VerificationSession(null, null, List.of(VerificationSession.Information.CAPTCHA), Collections.emptyList(), null, null, false, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); @@ -591,6 +591,7 @@ class VerificationControllerTest { when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( Optional.of(new VerificationSession("challenge", + null, List.of(VerificationSession.Information.CAPTCHA), List.of(VerificationSession.Information.PUSH_CHALLENGE), null, null, false, @@ -635,7 +636,7 @@ class VerificationControllerTest { Optional.of(registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession("challenge", List.of(), List.of(), null, null, true, + Optional.of(new VerificationSession("challenge", null, List.of(), List.of(), null, null, true, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); when(verificationSessionManager.update(any(), any())) @@ -672,6 +673,7 @@ class VerificationControllerTest { when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( Optional.of(new VerificationSession("challenge", + null, List.of(VerificationSession.Information.PUSH_CHALLENGE, VerificationSession.Information.CAPTCHA), Collections.emptyList(), null, null, false, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); @@ -715,7 +717,7 @@ class VerificationControllerTest { registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, List.of(VerificationSession.Information.CAPTCHA), + Optional.of(new VerificationSession(null, null, List.of(VerificationSession.Information.CAPTCHA), Collections.emptyList(), null, null, false, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); @@ -763,6 +765,7 @@ class VerificationControllerTest { when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( Optional.of(new VerificationSession("challenge", + null, List.of(VerificationSession.Information.CAPTCHA, VerificationSession.Information.CAPTCHA), Collections.emptyList(), null, null, false, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); @@ -810,6 +813,7 @@ class VerificationControllerTest { when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( Optional.of(new VerificationSession(null, + null, List.of(VerificationSession.Information.CAPTCHA), Collections.emptyList(), null, null, false, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); @@ -966,7 +970,7 @@ class VerificationControllerTest { .thenReturn(CompletableFuture.completedFuture(Optional.of(registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, Collections.emptyList(), Collections.emptyList(), null, null, true, + Optional.of(new VerificationSession(null, null, Collections.emptyList(), Collections.emptyList(), null, null, true, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); when(registrationServiceClient.sendVerificationCode(any(), any(), any(), any(), any(), any())) .thenReturn(CompletableFuture.completedFuture(registrationServiceSession)); @@ -996,8 +1000,8 @@ class VerificationControllerTest { Optional.of( registrationServiceSession))); when(verificationSessionManager.findForId(any())) - .thenReturn(CompletableFuture.completedFuture(Optional.of(new VerificationSession(null, List.of( - VerificationSession.Information.CAPTCHA), Collections.emptyList(), null, null, false, clock.millis(), clock.millis(), + .thenReturn(CompletableFuture.completedFuture(Optional.of(new VerificationSession(null, null, + List.of(VerificationSession.Information.CAPTCHA), Collections.emptyList(), null, null, false, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); final Invocation.Builder request = resources.getJerseyTest() @@ -1028,7 +1032,7 @@ class VerificationControllerTest { registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, Collections.emptyList(), Collections.emptyList(), null, null, false, + Optional.of(new VerificationSession(null, null, Collections.emptyList(), Collections.emptyList(), null, null, false, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); final Invocation.Builder request = resources.getJerseyTest() @@ -1056,7 +1060,7 @@ class VerificationControllerTest { .thenReturn(CompletableFuture.completedFuture(Optional.of(registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, Collections.emptyList(), Collections.emptyList(), null, null, true, + Optional.of(new VerificationSession(null, null, Collections.emptyList(), Collections.emptyList(), null, null, true, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); when(registrationServiceClient.sendVerificationCode(any(), any(), any(), any(), any(), any())) .thenReturn(CompletableFuture.failedFuture( @@ -1088,7 +1092,7 @@ class VerificationControllerTest { .thenReturn(CompletableFuture.completedFuture(Optional.of(registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, Collections.emptyList(), Collections.emptyList(), null, null, true, + Optional.of(new VerificationSession(null, null, Collections.emptyList(), Collections.emptyList(), null, null, true, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); when(registrationServiceClient.sendVerificationCode(any(), any(), any(), any(), any(), any())) .thenReturn(CompletableFuture.failedFuture( @@ -1120,7 +1124,7 @@ class VerificationControllerTest { .thenReturn(CompletableFuture.completedFuture(Optional.of(registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, Collections.emptyList(), Collections.emptyList(), null, null, true, + Optional.of(new VerificationSession(null, null, Collections.emptyList(), Collections.emptyList(), null, null, true, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); when(registrationServiceClient.sendVerificationCode(any(), any(), any(), any(), any(), any())) .thenReturn(CompletableFuture.completedFuture(registrationServiceSession)); @@ -1153,7 +1157,7 @@ class VerificationControllerTest { Optional.of(registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, Collections.emptyList(), Collections.emptyList(), null, null, true, + Optional.of(new VerificationSession(null, null, Collections.emptyList(), Collections.emptyList(), null, null, true, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); when(registrationServiceClient.sendVerificationCode(any(), any(), any(), any(), any(), any())) @@ -1197,7 +1201,7 @@ class VerificationControllerTest { .thenReturn(CompletableFuture.completedFuture(Optional.of(registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, Collections.emptyList(), Collections.emptyList(), null, null, true, + Optional.of(new VerificationSession(null, null, Collections.emptyList(), Collections.emptyList(), null, null, true, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); when(registrationServiceClient.sendVerificationCode(any(), any(), any(), any(), any(), any())) @@ -1231,7 +1235,7 @@ class VerificationControllerTest { Optional.of(registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, Collections.emptyList(), Collections.emptyList(), null, null, true, + Optional.of(new VerificationSession(null, null, Collections.emptyList(), Collections.emptyList(), null, null, true, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); when(registrationServiceClient.checkVerificationCode(any(), any(), any())) @@ -1258,7 +1262,7 @@ class VerificationControllerTest { Optional.of(registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, Collections.emptyList(), Collections.emptyList(), null, null, true, + Optional.of(new VerificationSession(null, null, Collections.emptyList(), Collections.emptyList(), null, null, true, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); final Invocation.Builder request = resources.getJerseyTest() @@ -1293,7 +1297,7 @@ class VerificationControllerTest { Optional.of(registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, Collections.emptyList(), Collections.emptyList(), null, null, true, + Optional.of(new VerificationSession(null, null, Collections.emptyList(), Collections.emptyList(), null, null, true, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); // There is no explicit indication in the exception that no code has been sent, but we treat all RegistrationServiceExceptions @@ -1330,7 +1334,7 @@ class VerificationControllerTest { Optional.of(registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, Collections.emptyList(), Collections.emptyList(), null, null, true, + Optional.of(new VerificationSession(null, null, Collections.emptyList(), Collections.emptyList(), null, null, true, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); when(registrationServiceClient.checkVerificationCode(any(), any(), any())) @@ -1356,7 +1360,7 @@ class VerificationControllerTest { Optional.of(registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, Collections.emptyList(), Collections.emptyList(), null, null, true, + Optional.of(new VerificationSession(null, null, Collections.emptyList(), Collections.emptyList(), null, null, true, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); when(registrationServiceClient.checkVerificationCode(any(), any(), any())) .thenReturn(CompletableFuture.failedFuture( @@ -1388,7 +1392,7 @@ class VerificationControllerTest { Optional.of(registrationServiceSession))); when(verificationSessionManager.findForId(any())) .thenReturn(CompletableFuture.completedFuture( - Optional.of(new VerificationSession(null, Collections.emptyList(), Collections.emptyList(), null, null, true, + Optional.of(new VerificationSession(null, null, Collections.emptyList(), Collections.emptyList(), null, null, true, clock.millis(), clock.millis(), registrationServiceSession.expiration())))); final RegistrationServiceSession verifiedSession = new RegistrationServiceSession(SESSION_ID, NUMBER, true, null, diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/VerificationSessionsTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/VerificationSessionsTest.java index 0c02a29d5..b5a9fb0b8 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/VerificationSessionsTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/VerificationSessionsTest.java @@ -6,6 +6,7 @@ package org.whispersystems.textsecuregcm.storage; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -22,6 +23,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.whispersystems.textsecuregcm.registration.VerificationSession; import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables; +import org.whispersystems.textsecuregcm.telephony.CarrierData; import org.whispersystems.textsecuregcm.util.ExceptionUtils; import software.amazon.awssdk.services.dynamodb.model.ConditionalCheckFailedException; @@ -46,7 +48,7 @@ class VerificationSessionsTest { final Instant updates = Instant.now(); final Duration remoteExpiration = Duration.ofMinutes(2); - final VerificationSession verificationSession = new VerificationSession(null, + final VerificationSession verificationSession = new VerificationSession(null, null, List.of(VerificationSession.Information.PUSH_CHALLENGE), Collections.emptyList(), null, null, true, created.toEpochMilli(), updates.toEpochMilli(), remoteExpiration.toSeconds()); @@ -63,7 +65,7 @@ class VerificationSessionsTest { final Optional absentSession = verificationSessions.findForKey(sessionId).join(); assertTrue(absentSession.isEmpty()); - final VerificationSession session = new VerificationSession(null, + final VerificationSession session = new VerificationSession(null, new CarrierData("Test", CarrierData.LineType.MOBILE, Optional.of("123"), Optional.empty()), List.of(VerificationSession.Information.PUSH_CHALLENGE), Collections.emptyList(), null, null, true, clock.millis(), clock.millis(), Duration.ofMinutes(1).toSeconds()); @@ -75,10 +77,10 @@ class VerificationSessionsTest { () -> verificationSessions.insert(sessionId, session).join()); final Throwable t = ExceptionUtils.unwrap(ce); - assertTrue(t instanceof ConditionalCheckFailedException, + assertInstanceOf(ConditionalCheckFailedException.class, t, "inserting with the same key should fail conditional checks"); - final VerificationSession updatedSession = new VerificationSession(null, Collections.emptyList(), + final VerificationSession updatedSession = new VerificationSession(null, new CarrierData("Test", CarrierData.LineType.MOBILE, Optional.of("123"), Optional.empty()), Collections.emptyList(), List.of(VerificationSession.Information.PUSH_CHALLENGE), null, null, true, clock.millis(), clock.millis(), Duration.ofMinutes(2).toSeconds()); verificationSessions.update(sessionId, updatedSession).join();