Handle "transport not allowed" responses from the registration service

This commit is contained in:
Jon Chambers 2023-05-16 18:54:01 -04:00 committed by Jon Chambers
parent 3e53884979
commit 300ac16cf1
5 changed files with 64 additions and 2 deletions

View File

@ -74,6 +74,7 @@ import org.whispersystems.textsecuregcm.registration.MessageTransport;
import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient;
import org.whispersystems.textsecuregcm.registration.RegistrationServiceException;
import org.whispersystems.textsecuregcm.registration.RegistrationServiceSenderException;
import org.whispersystems.textsecuregcm.registration.TransportNotAllowedException;
import org.whispersystems.textsecuregcm.registration.VerificationSession;
import org.whispersystems.textsecuregcm.spam.Extract;
import org.whispersystems.textsecuregcm.spam.FilterSpam;
@ -488,8 +489,13 @@ public class VerificationController {
throw registrationServiceException.getRegistrationSession()
.map(s -> buildResponse(s, verificationSession))
.map(verificationSessionResponse -> new ClientErrorException(
Response.status(Response.Status.CONFLICT).entity(verificationSessionResponse).build()))
.map(verificationSessionResponse -> {
final Response response = registrationServiceException instanceof TransportNotAllowedException
? Response.status(418).entity(verificationSessionResponse).build()
: Response.status(Response.Status.CONFLICT).entity(verificationSessionResponse).build();
return new ClientErrorException(response);
})
.orElseGet(NotFoundException::new);
} else if (unwrappedException instanceof RegistrationServiceSenderException) {

View File

@ -157,6 +157,8 @@ public class RegistrationServiceClient implements Managed {
RegistrationServiceSenderException.illegalArgument(response.getError().getMayRetry()));
case SEND_VERIFICATION_CODE_ERROR_TYPE_UNSPECIFIED -> throw new CompletionException(
RegistrationServiceSenderException.unknown(response.getError().getMayRetry()));
case SEND_VERIFICATION_CODE_ERROR_TYPE_TRANSPORT_NOT_ALLOWED -> throw new CompletionException(
new TransportNotAllowedException(buildSessionResponseFromMetadata(response.getSessionMetadata())));
default -> throw new CompletionException(
new RuntimeException("Failed to send verification code: " + response.getError().getErrorType()));

View File

@ -0,0 +1,14 @@
package org.whispersystems.textsecuregcm.registration;
import org.whispersystems.textsecuregcm.entities.RegistrationServiceSession;
/**
* Indicates that a request to send a verification code failed because the destination number does not support the
* requested transport (e.g. the caller asked to send an SMS to a landline number).
*/
public class TransportNotAllowedException extends RegistrationServiceException {
public TransportNotAllowedException(RegistrationServiceSession registrationServiceSession) {
super(registrationServiceSession);
}
}

View File

@ -318,6 +318,13 @@ enum SendVerificationCodeErrorType {
* been verified.
*/
SEND_VERIFICATION_CODE_ERROR_TYPE_SESSION_ALREADY_VERIFIED = 5;
/**
* A verification code could not be sent via the requested transport because
* the destination phone number (or the sender) does not support the requested
* transport.
*/
SEND_VERIFICATION_CODE_ERROR_TYPE_TRANSPORT_NOT_ALLOWED = 6;
}
message CheckVerificationCodeRequest {

View File

@ -68,6 +68,7 @@ import org.whispersystems.textsecuregcm.push.PushNotificationManager;
import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient;
import org.whispersystems.textsecuregcm.registration.RegistrationServiceException;
import org.whispersystems.textsecuregcm.registration.RegistrationServiceSenderException;
import org.whispersystems.textsecuregcm.registration.TransportNotAllowedException;
import org.whispersystems.textsecuregcm.registration.VerificationSession;
import org.whispersystems.textsecuregcm.spam.ScoreThresholdProvider;
import org.whispersystems.textsecuregcm.storage.Account;
@ -983,6 +984,38 @@ class VerificationControllerTest {
}
}
@Test
void requestVerificationCodeTransportNotAllowed() {
final String encodedSessionId = encodeSessionId(SESSION_ID);
final RegistrationServiceSession registrationServiceSession = new RegistrationServiceSession(SESSION_ID, NUMBER,
false, null, null,
null, SESSION_EXPIRATION_SECONDS);
when(registrationServiceClient.getSession(any(), any()))
.thenReturn(CompletableFuture.completedFuture(Optional.of(registrationServiceSession)));
when(verificationSessionManager.findForId(any()))
.thenReturn(CompletableFuture.completedFuture(
Optional.of(new VerificationSession(null, Collections.emptyList(), Collections.emptyList(), true,
clock.millis(), clock.millis(), registrationServiceSession.expiration()))));
when(registrationServiceClient.sendVerificationCode(any(), any(), any(), any(), any()))
.thenReturn(CompletableFuture.failedFuture(
new CompletionException(new TransportNotAllowedException(registrationServiceSession))));
final Invocation.Builder request = resources.getJerseyTest()
.target("/v1/verification/session/" + encodedSessionId + "/code")
.request()
.header(HttpHeaders.X_FORWARDED_FOR, "127.0.0.1");
try (final Response response = request.post(Entity.json(requestVerificationCodeJson("sms", "android")))) {
assertEquals(418, response.getStatus());
final VerificationSessionResponse verificationSessionResponse =
response.readEntity(VerificationSessionResponse.class);
assertTrue(verificationSessionResponse.allowedToRequestCode());
assertTrue(verificationSessionResponse.requestedInformation().isEmpty());
}
}
@Test
void requestVerificationCodeSuccess() {
final String encodedSessionId = encodeSessionId(SESSION_ID);