Support multiple sequential captcha challenges.
This commit is contained in:
parent
89ad213994
commit
d1d73fef30
5 changed files with 53 additions and 2 deletions
|
@ -8,6 +8,7 @@ import org.whispersystems.signalservice.api.push.exceptions.MustRequestNewCodeEx
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.NoSuchSessionException
|
import org.whispersystems.signalservice.api.push.exceptions.NoSuchSessionException
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.NonNormalizedPhoneNumberException
|
import org.whispersystems.signalservice.api.push.exceptions.NonNormalizedPhoneNumberException
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.RateLimitException
|
import org.whispersystems.signalservice.api.push.exceptions.RateLimitException
|
||||||
|
import org.whispersystems.signalservice.api.push.exceptions.TokenNotAcceptedException
|
||||||
import org.whispersystems.signalservice.api.util.Preconditions
|
import org.whispersystems.signalservice.api.util.Preconditions
|
||||||
import org.whispersystems.signalservice.internal.ServiceResponse
|
import org.whispersystems.signalservice.internal.ServiceResponse
|
||||||
import org.whispersystems.signalservice.internal.ServiceResponseProcessor
|
import org.whispersystems.signalservice.internal.ServiceResponseProcessor
|
||||||
|
@ -41,6 +42,10 @@ sealed class RegistrationSessionProcessor(response: ServiceResponse<Registration
|
||||||
return PUSH_CHALLENGE_KEY == getChallenge()
|
return PUSH_CHALLENGE_KEY == getChallenge()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isTokenRejected(): Boolean {
|
||||||
|
return error is TokenNotAcceptedException
|
||||||
|
}
|
||||||
|
|
||||||
fun isImpossibleNumber(): Boolean {
|
fun isImpossibleNumber(): Boolean {
|
||||||
return error is ImpossiblePhoneNumberException
|
return error is ImpossiblePhoneNumberException
|
||||||
}
|
}
|
||||||
|
|
|
@ -299,6 +299,9 @@ public final class EnterPhoneNumberFragment extends LoggingFragment implements R
|
||||||
String.format(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid), viewModel.getNumber().getFullFormattedNumber()));
|
String.format(getString(R.string.RegistrationActivity_the_number_you_specified_s_is_invalid), viewModel.getNumber().getFullFormattedNumber()));
|
||||||
} else if (processor.isNonNormalizedNumber()) {
|
} else if (processor.isNonNormalizedNumber()) {
|
||||||
handleNonNormalizedNumberError(processor.getOriginalNumber(), processor.getNormalizedNumber(), mode);
|
handleNonNormalizedNumberError(processor.getOriginalNumber(), processor.getNormalizedNumber(), mode);
|
||||||
|
} else if (processor.isTokenRejected()) {
|
||||||
|
Log.i(TAG, "The server did not accept the information.", processor.getError());
|
||||||
|
showErrorDialog(register.getContext(), getString(R.string.RegistrationActivity_we_need_to_verify_that_youre_human));
|
||||||
} else {
|
} else {
|
||||||
Log.i(TAG, "Unknown error during verification code request", processor.getError());
|
Log.i(TAG, "Unknown error during verification code request", processor.getError());
|
||||||
showErrorDialog(register.getContext(), getString(R.string.RegistrationActivity_unable_to_connect_to_service));
|
showErrorDialog(register.getContext(), getString(R.string.RegistrationActivity_unable_to_connect_to_service));
|
||||||
|
|
|
@ -10,6 +10,7 @@ import com.google.i18n.phonenumbers.NumberParseException;
|
||||||
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
import com.google.i18n.phonenumbers.PhoneNumberUtil;
|
||||||
import com.google.i18n.phonenumbers.Phonenumber;
|
import com.google.i18n.phonenumbers.Phonenumber;
|
||||||
|
|
||||||
|
import org.signal.core.util.logging.Log;
|
||||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||||
import org.thoughtcrime.securesms.pin.KbsRepository;
|
import org.thoughtcrime.securesms.pin.KbsRepository;
|
||||||
import org.thoughtcrime.securesms.pin.TokenData;
|
import org.thoughtcrime.securesms.pin.TokenData;
|
||||||
|
@ -36,6 +37,8 @@ import io.reactivex.rxjava3.core.Single;
|
||||||
*/
|
*/
|
||||||
public abstract class BaseRegistrationViewModel extends ViewModel {
|
public abstract class BaseRegistrationViewModel extends ViewModel {
|
||||||
|
|
||||||
|
private static final String TAG = Log.tag(BaseRegistrationViewModel.class);
|
||||||
|
|
||||||
private static final String STATE_NUMBER = "NUMBER";
|
private static final String STATE_NUMBER = "NUMBER";
|
||||||
private static final String STATE_REGISTRATION_SECRET = "REGISTRATION_SECRET";
|
private static final String STATE_REGISTRATION_SECRET = "REGISTRATION_SECRET";
|
||||||
private static final String STATE_VERIFICATION_CODE = "TEXT_CODE_ENTERED";
|
private static final String STATE_VERIFICATION_CODE = "TEXT_CODE_ENTERED";
|
||||||
|
@ -280,10 +283,14 @@ public abstract class BaseRegistrationViewModel extends ViewModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasCaptchaToken() && processor.captchaRequired()) {
|
if (hasCaptchaToken() && processor.captchaRequired()) {
|
||||||
return verifyAccountRepository.verifyCaptcha(sessionId, Objects.requireNonNull(getCaptchaToken()), e164, getRegistrationSecret())
|
Log.d(TAG, "Submitting completed captcha challenge");
|
||||||
|
final String captcha = Objects.requireNonNull(getCaptchaToken());
|
||||||
|
clearCaptchaResponse();
|
||||||
|
return verifyAccountRepository.verifyCaptcha(sessionId, captcha, e164, getRegistrationSecret())
|
||||||
.map(RegistrationSessionProcessor.RegistrationSessionProcessorForSession::new);
|
.map(RegistrationSessionProcessor.RegistrationSessionProcessorForSession::new);
|
||||||
} else {
|
} else {
|
||||||
String challenge = processor.getChallenge();
|
String challenge = processor.getChallenge();
|
||||||
|
Log.d(TAG, "Handling challenge of type " + challenge);
|
||||||
if (challenge != null) {
|
if (challenge != null) {
|
||||||
switch (challenge) {
|
switch (challenge) {
|
||||||
case RegistrationSessionProcessor.PUSH_CHALLENGE_KEY:
|
case RegistrationSessionProcessor.PUSH_CHALLENGE_KEY:
|
||||||
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
package org.whispersystems.signalservice.api.push.exceptions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception representing that the submitted information was not accepted (e.g. the push challenge token or captcha did not match)
|
||||||
|
*/
|
||||||
|
class TokenNotAcceptedException : NonSuccessfulResponseCodeException(403)
|
|
@ -86,6 +86,7 @@ import org.whispersystems.signalservice.api.push.exceptions.RateLimitException;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.RemoteAttestationResponseExpiredException;
|
import org.whispersystems.signalservice.api.push.exceptions.RemoteAttestationResponseExpiredException;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.ResumeLocationInvalidException;
|
import org.whispersystems.signalservice.api.push.exceptions.ResumeLocationInvalidException;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
|
import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException;
|
||||||
|
import org.whispersystems.signalservice.api.push.exceptions.TokenNotAcceptedException;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
import org.whispersystems.signalservice.api.push.exceptions.UnregisteredUserException;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.UsernameIsNotAssociatedWithAnAccountException;
|
import org.whispersystems.signalservice.api.push.exceptions.UsernameIsNotAssociatedWithAnAccountException;
|
||||||
import org.whispersystems.signalservice.api.push.exceptions.UsernameIsNotReservedException;
|
import org.whispersystems.signalservice.api.push.exceptions.UsernameIsNotReservedException;
|
||||||
|
@ -348,7 +349,7 @@ public class PushServiceSocket {
|
||||||
String path = VERIFICATION_SESSION_PATH + "/" + sessionId;
|
String path = VERIFICATION_SESSION_PATH + "/" + sessionId;
|
||||||
|
|
||||||
final UpdateVerificationSessionRequestBody requestBody = new UpdateVerificationSessionRequestBody(captchaToken, pushToken, pushChallengeToken, mcc, mnc);
|
final UpdateVerificationSessionRequestBody requestBody = new UpdateVerificationSessionRequestBody(captchaToken, pushToken, pushChallengeToken, mcc, mnc);
|
||||||
try (Response response = makeServiceRequest(path, "PATCH", jsonRequestBody(JsonUtil.toJson(requestBody)), NO_HEADERS, new RegistrationSessionResponseHandler(), Optional.empty(), false)) {
|
try (Response response = makeServiceRequest(path, "PATCH", jsonRequestBody(JsonUtil.toJson(requestBody)), NO_HEADERS, new PatchRegistrationSessionResponseHandler(), Optional.empty(), false)) {
|
||||||
return parseSessionMetadataResponse(response);
|
return parseSessionMetadataResponse(response);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2543,6 +2544,35 @@ public class PushServiceSocket {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static class PatchRegistrationSessionResponseHandler implements ResponseCodeHandler {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(int responseCode, ResponseBody body) throws NonSuccessfulResponseCodeException, PushNetworkException {
|
||||||
|
switch (responseCode) {
|
||||||
|
case 403:
|
||||||
|
throw new TokenNotAcceptedException();
|
||||||
|
case 404:
|
||||||
|
throw new NoSuchSessionException();
|
||||||
|
case 409:
|
||||||
|
RegistrationSessionMetadataJson response;
|
||||||
|
try {
|
||||||
|
response = JsonUtil.fromJson(body.string(), RegistrationSessionMetadataJson.class);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.e(TAG, "Unable to read response body.", e);
|
||||||
|
throw new NonSuccessfulResponseCodeException(409);
|
||||||
|
}
|
||||||
|
if (response.pushChallengedRequired()) {
|
||||||
|
throw new PushChallengeRequiredException();
|
||||||
|
} else if (response.captchaRequired()) {
|
||||||
|
throw new CaptchaRequiredException();
|
||||||
|
} else {
|
||||||
|
throw new HttpConflictException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static class RegistrationCodeRequestResponseHandler implements ResponseCodeHandler {
|
private static class RegistrationCodeRequestResponseHandler implements ResponseCodeHandler {
|
||||||
@Override public void handle(int responseCode, ResponseBody body) throws NonSuccessfulResponseCodeException, PushNetworkException {
|
@Override public void handle(int responseCode, ResponseBody body) throws NonSuccessfulResponseCodeException, PushNetworkException {
|
||||||
switch (responseCode) {
|
switch (responseCode) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue