Read registration recovery passwords exclusively by PNI
This commit is contained in:
parent
6967e4e54b
commit
5b9f8177f2
|
@ -8,6 +8,7 @@ package org.signal.integration;
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import org.signal.integration.config.Config;
|
import org.signal.integration.config.Config;
|
||||||
import org.whispersystems.textsecuregcm.metrics.NoopAwsSdkMetricPublisher;
|
import org.whispersystems.textsecuregcm.metrics.NoopAwsSdkMetricPublisher;
|
||||||
|
@ -20,7 +21,6 @@ import org.whispersystems.textsecuregcm.storage.VerificationSessions;
|
||||||
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
|
||||||
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
|
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
|
||||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||||
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
|
|
||||||
|
|
||||||
public class IntegrationTools {
|
public class IntegrationTools {
|
||||||
|
|
||||||
|
@ -57,8 +57,8 @@ public class IntegrationTools {
|
||||||
this.verificationSessionManager = verificationSessionManager;
|
this.verificationSessionManager = verificationSessionManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<Void> populateRecoveryPassword(final String e164, final byte[] password) {
|
public CompletableFuture<Void> populateRecoveryPassword(final String number, final byte[] password) {
|
||||||
return registrationRecoveryPasswordsManager.storeForCurrentNumber(e164, password);
|
return registrationRecoveryPasswordsManager.storeForCurrentNumber(number, password);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<Optional<String>> peekVerificationSessionPushChallenge(final String sessionId) {
|
public CompletableFuture<Optional<String>> peekVerificationSessionPushChallenge(final String sessionId) {
|
||||||
|
|
|
@ -78,7 +78,7 @@ public final class Operations {
|
||||||
final TestUser user = TestUser.create(number, accountPassword, registrationPassword);
|
final TestUser user = TestUser.create(number, accountPassword, registrationPassword);
|
||||||
final AccountAttributes accountAttributes = user.accountAttributes();
|
final AccountAttributes accountAttributes = user.accountAttributes();
|
||||||
|
|
||||||
INTEGRATION_TOOLS.populateRecoveryPassword(number, registrationPassword).join();
|
INTEGRATION_TOOLS.populateRecoveryPassword(user.phoneNumber(), registrationPassword).join();
|
||||||
|
|
||||||
final ECKeyPair aciIdentityKeyPair = Curve.generateKeyPair();
|
final ECKeyPair aciIdentityKeyPair = Curve.generateKeyPair();
|
||||||
final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair();
|
final ECKeyPair pniIdentityKeyPair = Curve.generateKeyPair();
|
||||||
|
|
|
@ -1076,7 +1076,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
|
|
||||||
|
|
||||||
final PhoneVerificationTokenManager phoneVerificationTokenManager = new PhoneVerificationTokenManager(
|
final PhoneVerificationTokenManager phoneVerificationTokenManager = new PhoneVerificationTokenManager(
|
||||||
registrationServiceClient, registrationRecoveryPasswordsManager, registrationRecoveryChecker);
|
phoneNumberIdentifiers, registrationServiceClient, registrationRecoveryPasswordsManager, registrationRecoveryChecker);
|
||||||
final List<Object> commonControllers = Lists.newArrayList(
|
final List<Object> commonControllers = Lists.newArrayList(
|
||||||
new AccountController(accountsManager, rateLimiters, turnTokenGenerator, registrationRecoveryPasswordsManager,
|
new AccountController(accountsManager, rateLimiters, turnTokenGenerator, registrationRecoveryPasswordsManager,
|
||||||
usernameHashZkProofVerifier),
|
usernameHashZkProofVerifier),
|
||||||
|
@ -1119,8 +1119,9 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
config.getCdnConfiguration().credentials().secretAccessKey().value(), config.getCdnConfiguration().region(),
|
config.getCdnConfiguration().credentials().secretAccessKey().value(), config.getCdnConfiguration().region(),
|
||||||
config.getCdnConfiguration().bucket()),
|
config.getCdnConfiguration().bucket()),
|
||||||
new VerificationController(registrationServiceClient, new VerificationSessionManager(verificationSessions),
|
new VerificationController(registrationServiceClient, new VerificationSessionManager(verificationSessions),
|
||||||
pushNotificationManager, registrationCaptchaManager, registrationRecoveryPasswordsManager, rateLimiters,
|
pushNotificationManager, registrationCaptchaManager, registrationRecoveryPasswordsManager,
|
||||||
accountsManager, registrationFraudChecker, dynamicConfigurationManager, clock)
|
phoneNumberIdentifiers, rateLimiters, accountsManager, registrationFraudChecker,
|
||||||
|
dynamicConfigurationManager, clock)
|
||||||
);
|
);
|
||||||
if (config.getSubscription() != null && config.getOneTimeDonations() != null) {
|
if (config.getSubscription() != null && config.getOneTimeDonations() != null) {
|
||||||
SubscriptionManager subscriptionManager = new SubscriptionManager(subscriptions,
|
SubscriptionManager subscriptionManager = new SubscriptionManager(subscriptions,
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.whispersystems.textsecuregcm.entities.PhoneVerificationRequest;
|
||||||
import org.whispersystems.textsecuregcm.entities.RegistrationServiceSession;
|
import org.whispersystems.textsecuregcm.entities.RegistrationServiceSession;
|
||||||
import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient;
|
import org.whispersystems.textsecuregcm.registration.RegistrationServiceClient;
|
||||||
import org.whispersystems.textsecuregcm.spam.RegistrationRecoveryChecker;
|
import org.whispersystems.textsecuregcm.spam.RegistrationRecoveryChecker;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.PhoneNumberIdentifiers;
|
||||||
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
||||||
|
|
||||||
public class PhoneVerificationTokenManager {
|
public class PhoneVerificationTokenManager {
|
||||||
|
@ -33,13 +34,17 @@ public class PhoneVerificationTokenManager {
|
||||||
private static final Duration REGISTRATION_RPC_TIMEOUT = Duration.ofSeconds(15);
|
private static final Duration REGISTRATION_RPC_TIMEOUT = Duration.ofSeconds(15);
|
||||||
private static final long VERIFICATION_TIMEOUT_SECONDS = REGISTRATION_RPC_TIMEOUT.plusSeconds(1).getSeconds();
|
private static final long VERIFICATION_TIMEOUT_SECONDS = REGISTRATION_RPC_TIMEOUT.plusSeconds(1).getSeconds();
|
||||||
|
|
||||||
|
private final PhoneNumberIdentifiers phoneNumberIdentifiers;
|
||||||
|
|
||||||
private final RegistrationServiceClient registrationServiceClient;
|
private final RegistrationServiceClient registrationServiceClient;
|
||||||
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager;
|
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager;
|
||||||
private final RegistrationRecoveryChecker registrationRecoveryChecker;
|
private final RegistrationRecoveryChecker registrationRecoveryChecker;
|
||||||
|
|
||||||
public PhoneVerificationTokenManager(final RegistrationServiceClient registrationServiceClient,
|
public PhoneVerificationTokenManager(final PhoneNumberIdentifiers phoneNumberIdentifiers,
|
||||||
|
final RegistrationServiceClient registrationServiceClient,
|
||||||
final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager,
|
final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager,
|
||||||
final RegistrationRecoveryChecker registrationRecoveryChecker) {
|
final RegistrationRecoveryChecker registrationRecoveryChecker) {
|
||||||
|
this.phoneNumberIdentifiers = phoneNumberIdentifiers;
|
||||||
this.registrationServiceClient = registrationServiceClient;
|
this.registrationServiceClient = registrationServiceClient;
|
||||||
this.registrationRecoveryPasswordsManager = registrationRecoveryPasswordsManager;
|
this.registrationRecoveryPasswordsManager = registrationRecoveryPasswordsManager;
|
||||||
this.registrationRecoveryChecker = registrationRecoveryChecker;
|
this.registrationRecoveryChecker = registrationRecoveryChecker;
|
||||||
|
@ -109,7 +114,7 @@ public class PhoneVerificationTokenManager {
|
||||||
throw new ForbiddenException("recoveryPassword couldn't be verified");
|
throw new ForbiddenException("recoveryPassword couldn't be verified");
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
final boolean verified = registrationRecoveryPasswordsManager.verify(number, recoveryPassword)
|
final boolean verified = registrationRecoveryPasswordsManager.verify(phoneNumberIdentifiers.getPhoneNumberIdentifier(number).join(), recoveryPassword)
|
||||||
.get(VERIFICATION_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
.get(VERIFICATION_TIMEOUT_SECONDS, TimeUnit.SECONDS);
|
||||||
if (!verified) {
|
if (!verified) {
|
||||||
throw new ForbiddenException("recoveryPassword couldn't be verified");
|
throw new ForbiddenException("recoveryPassword couldn't be verified");
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
||||||
import org.whispersystems.textsecuregcm.entities.PhoneVerificationRequest;
|
import org.whispersystems.textsecuregcm.entities.PhoneVerificationRequest;
|
||||||
import org.whispersystems.textsecuregcm.entities.RegistrationLockFailure;
|
import org.whispersystems.textsecuregcm.entities.RegistrationLockFailure;
|
||||||
import org.whispersystems.textsecuregcm.entities.Svr3Credentials;
|
import org.whispersystems.textsecuregcm.entities.Svr3Credentials;
|
||||||
|
import org.whispersystems.textsecuregcm.identity.IdentityType;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||||
import org.whispersystems.textsecuregcm.metrics.UserAgentTagUtil;
|
import org.whispersystems.textsecuregcm.metrics.UserAgentTagUtil;
|
||||||
import org.whispersystems.textsecuregcm.push.NotPushRegisteredException;
|
import org.whispersystems.textsecuregcm.push.NotPushRegisteredException;
|
||||||
|
|
|
@ -83,6 +83,7 @@ import org.whispersystems.textsecuregcm.spam.RegistrationFraudChecker;
|
||||||
import org.whispersystems.textsecuregcm.spam.RegistrationFraudChecker.VerificationCheck;
|
import org.whispersystems.textsecuregcm.spam.RegistrationFraudChecker.VerificationCheck;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.PhoneNumberIdentifiers;
|
||||||
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.VerificationSessionManager;
|
import org.whispersystems.textsecuregcm.storage.VerificationSessionManager;
|
||||||
import org.whispersystems.textsecuregcm.util.ExceptionUtils;
|
import org.whispersystems.textsecuregcm.util.ExceptionUtils;
|
||||||
|
@ -116,6 +117,7 @@ public class VerificationController {
|
||||||
private final PushNotificationManager pushNotificationManager;
|
private final PushNotificationManager pushNotificationManager;
|
||||||
private final RegistrationCaptchaManager registrationCaptchaManager;
|
private final RegistrationCaptchaManager registrationCaptchaManager;
|
||||||
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager;
|
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager;
|
||||||
|
private final PhoneNumberIdentifiers phoneNumberIdentifiers;
|
||||||
private final RateLimiters rateLimiters;
|
private final RateLimiters rateLimiters;
|
||||||
private final AccountsManager accountsManager;
|
private final AccountsManager accountsManager;
|
||||||
private final RegistrationFraudChecker registrationFraudChecker;
|
private final RegistrationFraudChecker registrationFraudChecker;
|
||||||
|
@ -127,6 +129,7 @@ public class VerificationController {
|
||||||
final PushNotificationManager pushNotificationManager,
|
final PushNotificationManager pushNotificationManager,
|
||||||
final RegistrationCaptchaManager registrationCaptchaManager,
|
final RegistrationCaptchaManager registrationCaptchaManager,
|
||||||
final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager,
|
final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager,
|
||||||
|
final PhoneNumberIdentifiers phoneNumberIdentifiers,
|
||||||
final RateLimiters rateLimiters,
|
final RateLimiters rateLimiters,
|
||||||
final AccountsManager accountsManager,
|
final AccountsManager accountsManager,
|
||||||
final RegistrationFraudChecker registrationFraudChecker,
|
final RegistrationFraudChecker registrationFraudChecker,
|
||||||
|
@ -137,6 +140,7 @@ public class VerificationController {
|
||||||
this.pushNotificationManager = pushNotificationManager;
|
this.pushNotificationManager = pushNotificationManager;
|
||||||
this.registrationCaptchaManager = registrationCaptchaManager;
|
this.registrationCaptchaManager = registrationCaptchaManager;
|
||||||
this.registrationRecoveryPasswordsManager = registrationRecoveryPasswordsManager;
|
this.registrationRecoveryPasswordsManager = registrationRecoveryPasswordsManager;
|
||||||
|
this.phoneNumberIdentifiers = phoneNumberIdentifiers;
|
||||||
this.rateLimiters = rateLimiters;
|
this.rateLimiters = rateLimiters;
|
||||||
this.accountsManager = accountsManager;
|
this.accountsManager = accountsManager;
|
||||||
this.registrationFraudChecker = registrationFraudChecker;
|
this.registrationFraudChecker = registrationFraudChecker;
|
||||||
|
@ -626,8 +630,8 @@ public class VerificationController {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws ClientErrorException with {@code 422} status if the ID cannot be decoded
|
* @throws ClientErrorException with {@code 422} status if the ID cannot be decoded
|
||||||
* @throws javax.ws.rs.NotFoundException if the ID cannot be found
|
* @throws NotFoundException if the ID cannot be found
|
||||||
*/
|
*/
|
||||||
private RegistrationServiceSession retrieveRegistrationServiceSession(final String encodedSessionId) {
|
private RegistrationServiceSession retrieveRegistrationServiceSession(final String encodedSessionId) {
|
||||||
final byte[] sessionId;
|
final byte[] sessionId;
|
||||||
|
|
|
@ -9,8 +9,11 @@ import java.util.Base64;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import org.signal.registration.rpc.RegistrationSessionMetadata;
|
import org.signal.registration.rpc.RegistrationSessionMetadata;
|
||||||
|
|
||||||
public record RegistrationServiceSession(byte[] id, String number, boolean verified,
|
public record RegistrationServiceSession(byte[] id,
|
||||||
@Nullable Long nextSms, @Nullable Long nextVoiceCall,
|
String number,
|
||||||
|
boolean verified,
|
||||||
|
@Nullable Long nextSms,
|
||||||
|
@Nullable Long nextVoiceCall,
|
||||||
@Nullable Long nextVerificationAttempt,
|
@Nullable Long nextVerificationAttempt,
|
||||||
long expiration) {
|
long expiration) {
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ import org.whispersystems.textsecuregcm.auth.grpc.AuthenticationUtil;
|
||||||
import org.whispersystems.textsecuregcm.controllers.AccountController;
|
import org.whispersystems.textsecuregcm.controllers.AccountController;
|
||||||
import org.whispersystems.textsecuregcm.entities.EncryptedUsername;
|
import org.whispersystems.textsecuregcm.entities.EncryptedUsername;
|
||||||
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
|
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
|
||||||
|
import org.whispersystems.textsecuregcm.identity.IdentityType;
|
||||||
import org.whispersystems.textsecuregcm.identity.PniServiceIdentifier;
|
import org.whispersystems.textsecuregcm.identity.PniServiceIdentifier;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
|
|
|
@ -16,7 +16,6 @@ import java.util.UUID;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
|
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
|
||||||
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
import org.whispersystems.textsecuregcm.util.AttributeValues;
|
||||||
import org.whispersystems.textsecuregcm.util.Pair;
|
|
||||||
import org.whispersystems.textsecuregcm.util.Util;
|
import org.whispersystems.textsecuregcm.util.Util;
|
||||||
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
|
||||||
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
|
||||||
|
@ -28,9 +27,8 @@ import software.amazon.awssdk.services.dynamodb.model.TransactWriteItemsRequest;
|
||||||
|
|
||||||
public class RegistrationRecoveryPasswords {
|
public class RegistrationRecoveryPasswords {
|
||||||
|
|
||||||
// As a temporary transitional measure, this can be either a string representation of an E164-formatted phone number
|
// For historical reasons, we record the PNI as a UUID string rather than a compact byte array
|
||||||
// or a UUID (PNI) string
|
static final String KEY_PNI = "P";
|
||||||
static final String KEY_E164 = "P";
|
|
||||||
static final String ATTR_EXP = "E";
|
static final String ATTR_EXP = "E";
|
||||||
static final String ATTR_SALT = "S";
|
static final String ATTR_SALT = "S";
|
||||||
static final String ATTR_HASH = "H";
|
static final String ATTR_HASH = "H";
|
||||||
|
@ -54,10 +52,10 @@ public class RegistrationRecoveryPasswords {
|
||||||
this.clock = requireNonNull(clock);
|
this.clock = requireNonNull(clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<Optional<SaltedTokenHash>> lookup(final String number) {
|
public CompletableFuture<Optional<SaltedTokenHash>> lookup(final UUID phoneNumberIdentifier) {
|
||||||
return asyncClient.getItem(GetItemRequest.builder()
|
return asyncClient.getItem(GetItemRequest.builder()
|
||||||
.tableName(tableName)
|
.tableName(tableName)
|
||||||
.key(Map.of(KEY_E164, AttributeValues.fromString(number)))
|
.key(Map.of(KEY_PNI, AttributeValues.fromString(phoneNumberIdentifier.toString())))
|
||||||
.consistentRead(true)
|
.consistentRead(true)
|
||||||
.build())
|
.build())
|
||||||
.thenApply(getItemResponse -> Optional.ofNullable(getItemResponse.item())
|
.thenApply(getItemResponse -> Optional.ofNullable(getItemResponse.item())
|
||||||
|
@ -66,10 +64,6 @@ public class RegistrationRecoveryPasswords {
|
||||||
.map(RegistrationRecoveryPasswords::saltedTokenHashFromItem));
|
.map(RegistrationRecoveryPasswords::saltedTokenHashFromItem));
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<Optional<SaltedTokenHash>> lookup(final UUID phoneNumberIdentifier) {
|
|
||||||
return lookup(phoneNumberIdentifier.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompletableFuture<Void> addOrReplace(final String number, final UUID phoneNumberIdentifier, final SaltedTokenHash data) {
|
public CompletableFuture<Void> addOrReplace(final String number, final UUID phoneNumberIdentifier, final SaltedTokenHash data) {
|
||||||
final long expirationSeconds = expirationSeconds();
|
final long expirationSeconds = expirationSeconds();
|
||||||
|
|
||||||
|
@ -90,7 +84,7 @@ public class RegistrationRecoveryPasswords {
|
||||||
.put(Put.builder()
|
.put(Put.builder()
|
||||||
.tableName(tableName)
|
.tableName(tableName)
|
||||||
.item(Map.of(
|
.item(Map.of(
|
||||||
KEY_E164, AttributeValues.fromString(key),
|
KEY_PNI, AttributeValues.fromString(key),
|
||||||
ATTR_EXP, AttributeValues.fromLong(expirationSeconds),
|
ATTR_EXP, AttributeValues.fromLong(expirationSeconds),
|
||||||
ATTR_SALT, AttributeValues.fromString(salt),
|
ATTR_SALT, AttributeValues.fromString(salt),
|
||||||
ATTR_HASH, AttributeValues.fromString(hash)))
|
ATTR_HASH, AttributeValues.fromString(hash)))
|
||||||
|
@ -111,7 +105,7 @@ public class RegistrationRecoveryPasswords {
|
||||||
return TransactWriteItem.builder()
|
return TransactWriteItem.builder()
|
||||||
.delete(Delete.builder()
|
.delete(Delete.builder()
|
||||||
.tableName(tableName)
|
.tableName(tableName)
|
||||||
.key(Map.of(KEY_E164, AttributeValues.fromString(key)))
|
.key(Map.of(KEY_PNI, AttributeValues.fromString(key)))
|
||||||
.build())
|
.build())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ import static java.util.Objects.requireNonNull;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.util.HexFormat;
|
import java.util.HexFormat;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -30,8 +31,8 @@ public class RegistrationRecoveryPasswordsManager {
|
||||||
this.phoneNumberIdentifiers = phoneNumberIdentifiers;
|
this.phoneNumberIdentifiers = phoneNumberIdentifiers;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompletableFuture<Boolean> verify(final String number, final byte[] password) {
|
public CompletableFuture<Boolean> verify(final UUID phoneNumberIdentifier, final byte[] password) {
|
||||||
return registrationRecoveryPasswords.lookup(number)
|
return registrationRecoveryPasswords.lookup(phoneNumberIdentifier)
|
||||||
.thenApply(maybeHash -> maybeHash.filter(hash -> hash.verify(bytesToString(password))))
|
.thenApply(maybeHash -> maybeHash.filter(hash -> hash.verify(bytesToString(password))))
|
||||||
.whenComplete((result, error) -> {
|
.whenComplete((result, error) -> {
|
||||||
if (error != null) {
|
if (error != null) {
|
||||||
|
|
|
@ -31,11 +31,11 @@ import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.MethodSource;
|
import org.junit.jupiter.params.provider.MethodSource;
|
||||||
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
||||||
import org.whispersystems.textsecuregcm.entities.PhoneVerificationRequest;
|
import org.whispersystems.textsecuregcm.entities.PhoneVerificationRequest;
|
||||||
|
import org.whispersystems.textsecuregcm.identity.IdentityType;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||||
import org.whispersystems.textsecuregcm.push.NotPushRegisteredException;
|
import org.whispersystems.textsecuregcm.push.NotPushRegisteredException;
|
||||||
import org.whispersystems.textsecuregcm.push.PushNotificationManager;
|
import org.whispersystems.textsecuregcm.push.PushNotificationManager;
|
||||||
import org.whispersystems.textsecuregcm.push.WebSocketConnectionEventManager;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.Device;
|
import org.whispersystems.textsecuregcm.storage.Device;
|
||||||
|
@ -53,7 +53,7 @@ class RegistrationLockVerificationManagerTest {
|
||||||
ExternalServiceCredentialsGenerator.class);
|
ExternalServiceCredentialsGenerator.class);
|
||||||
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager = mock(
|
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager = mock(
|
||||||
RegistrationRecoveryPasswordsManager.class);
|
RegistrationRecoveryPasswordsManager.class);
|
||||||
private static PushNotificationManager pushNotificationManager = mock(PushNotificationManager.class);
|
private final PushNotificationManager pushNotificationManager = mock(PushNotificationManager.class);
|
||||||
private final RateLimiters rateLimiters = mock(RateLimiters.class);
|
private final RateLimiters rateLimiters = mock(RateLimiters.class);
|
||||||
private final RegistrationLockVerificationManager registrationLockVerificationManager = new RegistrationLockVerificationManager(
|
private final RegistrationLockVerificationManager registrationLockVerificationManager = new RegistrationLockVerificationManager(
|
||||||
accountsManager, disconnectionRequestManager, svr2CredentialsGenerator,
|
accountsManager, disconnectionRequestManager, svr2CredentialsGenerator,
|
||||||
|
@ -105,12 +105,13 @@ class RegistrationLockVerificationManagerTest {
|
||||||
if (!verificationType.equals(PhoneVerificationRequest.VerificationType.RECOVERY_PASSWORD) || clientRegistrationLock != null) {
|
if (!verificationType.equals(PhoneVerificationRequest.VerificationType.RECOVERY_PASSWORD) || clientRegistrationLock != null) {
|
||||||
verify(registrationRecoveryPasswordsManager).removeForNumber(account.getNumber());
|
verify(registrationRecoveryPasswordsManager).removeForNumber(account.getNumber());
|
||||||
} else {
|
} else {
|
||||||
verify(registrationRecoveryPasswordsManager, never()).removeForNumber(account.getNumber());
|
verify(registrationRecoveryPasswordsManager, never()).removeForNumber(any());
|
||||||
}
|
}
|
||||||
verify(disconnectionRequestManager).requestDisconnection(account.getUuid(), List.of(Device.PRIMARY_ID));
|
verify(disconnectionRequestManager).requestDisconnection(account.getUuid(), List.of(Device.PRIMARY_ID));
|
||||||
try {
|
try {
|
||||||
verify(pushNotificationManager).sendAttemptLoginNotification(any(), eq("failedRegistrationLock"));
|
verify(pushNotificationManager).sendAttemptLoginNotification(any(), eq("failedRegistrationLock"));
|
||||||
} catch (NotPushRegisteredException npre) {}
|
} catch (final NotPushRegisteredException ignored) {
|
||||||
|
}
|
||||||
if (alreadyLocked) {
|
if (alreadyLocked) {
|
||||||
verify(account, never()).lockAuthTokenHash();
|
verify(account, never()).lockAuthTokenHash();
|
||||||
} else {
|
} else {
|
||||||
|
@ -126,10 +127,13 @@ class RegistrationLockVerificationManagerTest {
|
||||||
doThrow(RateLimitExceededException.class).when(pinLimiter).validate(anyString());
|
doThrow(RateLimitExceededException.class).when(pinLimiter).validate(anyString());
|
||||||
yield new Pair<>(RateLimitExceededException.class, ignored -> {
|
yield new Pair<>(RateLimitExceededException.class, ignored -> {
|
||||||
verify(account, never()).lockAuthTokenHash();
|
verify(account, never()).lockAuthTokenHash();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
verify(pushNotificationManager, never()).sendAttemptLoginNotification(any(), eq("failedRegistrationLock"));
|
verify(pushNotificationManager, never()).sendAttemptLoginNotification(any(), eq("failedRegistrationLock"));
|
||||||
} catch (NotPushRegisteredException npre) {}
|
} catch (final NotPushRegisteredException ignored2) {
|
||||||
verify(registrationRecoveryPasswordsManager, never()).removeForNumber(account.getNumber());
|
}
|
||||||
|
|
||||||
|
verify(registrationRecoveryPasswordsManager, never()).removeForNumber(any());
|
||||||
verify(disconnectionRequestManager, never()).requestDisconnection(any(), any());
|
verify(disconnectionRequestManager, never()).requestDisconnection(any(), any());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -167,7 +171,7 @@ class RegistrationLockVerificationManagerTest {
|
||||||
PhoneVerificationRequest.VerificationType.SESSION));
|
PhoneVerificationRequest.VerificationType.SESSION));
|
||||||
|
|
||||||
verify(account, never()).lockAuthTokenHash();
|
verify(account, never()).lockAuthTokenHash();
|
||||||
verify(registrationRecoveryPasswordsManager, never()).removeForNumber(account.getNumber());
|
verify(registrationRecoveryPasswordsManager, never()).removeForNumber(any());
|
||||||
verify(disconnectionRequestManager, never()).requestDisconnection(any(), any());
|
verify(disconnectionRequestManager, never()).requestDisconnection(any(), any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,7 @@ import org.whispersystems.textsecuregcm.storage.AccountBadge;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.ChangeNumberManager;
|
import org.whispersystems.textsecuregcm.storage.ChangeNumberManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.Device;
|
import org.whispersystems.textsecuregcm.storage.Device;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.PhoneNumberIdentifiers;
|
||||||
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
|
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||||
|
@ -105,6 +106,7 @@ class AccountControllerV2Test {
|
||||||
|
|
||||||
private final AccountsManager accountsManager = mock(AccountsManager.class);
|
private final AccountsManager accountsManager = mock(AccountsManager.class);
|
||||||
private final ChangeNumberManager changeNumberManager = mock(ChangeNumberManager.class);
|
private final ChangeNumberManager changeNumberManager = mock(ChangeNumberManager.class);
|
||||||
|
private final PhoneNumberIdentifiers phoneNumberIdentifiers = mock(PhoneNumberIdentifiers.class);
|
||||||
private final RegistrationServiceClient registrationServiceClient = mock(RegistrationServiceClient.class);
|
private final RegistrationServiceClient registrationServiceClient = mock(RegistrationServiceClient.class);
|
||||||
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager = mock(
|
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager = mock(
|
||||||
RegistrationRecoveryPasswordsManager.class);
|
RegistrationRecoveryPasswordsManager.class);
|
||||||
|
@ -125,8 +127,8 @@ class AccountControllerV2Test {
|
||||||
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
||||||
.addResource(
|
.addResource(
|
||||||
new AccountControllerV2(accountsManager, changeNumberManager,
|
new AccountControllerV2(accountsManager, changeNumberManager,
|
||||||
new PhoneVerificationTokenManager(registrationServiceClient, registrationRecoveryPasswordsManager,
|
new PhoneVerificationTokenManager(phoneNumberIdentifiers, registrationServiceClient,
|
||||||
registrationRecoveryChecker),
|
registrationRecoveryPasswordsManager, registrationRecoveryChecker),
|
||||||
registrationLockVerificationManager, rateLimiters))
|
registrationLockVerificationManager, rateLimiters))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -401,6 +403,8 @@ class AccountControllerV2Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void recoveryPasswordManagerVerificationTrue() throws Exception {
|
void recoveryPasswordManagerVerificationTrue() throws Exception {
|
||||||
|
when(phoneNumberIdentifiers.getPhoneNumberIdentifier(any()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(UUID.randomUUID()));
|
||||||
when(registrationRecoveryPasswordsManager.verify(any(), any()))
|
when(registrationRecoveryPasswordsManager.verify(any(), any()))
|
||||||
.thenReturn(CompletableFuture.completedFuture(true));
|
.thenReturn(CompletableFuture.completedFuture(true));
|
||||||
when(registrationRecoveryChecker.checkRegistrationRecoveryAttempt(any(), any()))
|
when(registrationRecoveryChecker.checkRegistrationRecoveryAttempt(any(), any()))
|
||||||
|
@ -443,6 +447,8 @@ class AccountControllerV2Test {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void registrationRecoveryCheckerAllowsAttempt() {
|
void registrationRecoveryCheckerAllowsAttempt() {
|
||||||
|
when(phoneNumberIdentifiers.getPhoneNumberIdentifier(any()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(UUID.randomUUID()));
|
||||||
when(registrationRecoveryChecker.checkRegistrationRecoveryAttempt(any(), any())).thenReturn(true);
|
when(registrationRecoveryChecker.checkRegistrationRecoveryAttempt(any(), any())).thenReturn(true);
|
||||||
when(registrationRecoveryPasswordsManager.verify(any(), any()))
|
when(registrationRecoveryPasswordsManager.verify(any(), any()))
|
||||||
.thenReturn(CompletableFuture.completedFuture(true));
|
.thenReturn(CompletableFuture.completedFuture(true));
|
||||||
|
|
|
@ -7,6 +7,7 @@ package org.whispersystems.textsecuregcm.controllers;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.ArgumentMatchers.argThat;
|
import static org.mockito.ArgumentMatchers.argThat;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.doThrow;
|
import static org.mockito.Mockito.doThrow;
|
||||||
|
@ -77,6 +78,7 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.Device;
|
import org.whispersystems.textsecuregcm.storage.Device;
|
||||||
import org.whispersystems.textsecuregcm.storage.DeviceCapability;
|
import org.whispersystems.textsecuregcm.storage.DeviceCapability;
|
||||||
import org.whispersystems.textsecuregcm.storage.DeviceSpec;
|
import org.whispersystems.textsecuregcm.storage.DeviceSpec;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.PhoneNumberIdentifiers;
|
||||||
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||||
import org.whispersystems.textsecuregcm.tests.util.KeysHelper;
|
import org.whispersystems.textsecuregcm.tests.util.KeysHelper;
|
||||||
|
@ -94,6 +96,7 @@ class RegistrationControllerTest {
|
||||||
private static final String PASSWORD = "password";
|
private static final String PASSWORD = "password";
|
||||||
|
|
||||||
private final AccountsManager accountsManager = mock(AccountsManager.class);
|
private final AccountsManager accountsManager = mock(AccountsManager.class);
|
||||||
|
private final PhoneNumberIdentifiers phoneNumberIdentifiers = mock(PhoneNumberIdentifiers.class);
|
||||||
private final RegistrationServiceClient registrationServiceClient = mock(RegistrationServiceClient.class);
|
private final RegistrationServiceClient registrationServiceClient = mock(RegistrationServiceClient.class);
|
||||||
private final RegistrationLockVerificationManager registrationLockVerificationManager = mock(
|
private final RegistrationLockVerificationManager registrationLockVerificationManager = mock(
|
||||||
RegistrationLockVerificationManager.class);
|
RegistrationLockVerificationManager.class);
|
||||||
|
@ -113,8 +116,8 @@ class RegistrationControllerTest {
|
||||||
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
||||||
.addResource(
|
.addResource(
|
||||||
new RegistrationController(accountsManager,
|
new RegistrationController(accountsManager,
|
||||||
new PhoneVerificationTokenManager(registrationServiceClient, registrationRecoveryPasswordsManager,
|
new PhoneVerificationTokenManager(phoneNumberIdentifiers, registrationServiceClient,
|
||||||
registrationRecoveryChecker),
|
registrationRecoveryPasswordsManager, registrationRecoveryChecker),
|
||||||
registrationLockVerificationManager, rateLimiters))
|
registrationLockVerificationManager, rateLimiters))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -243,6 +246,8 @@ class RegistrationControllerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void recoveryPasswordManagerVerificationFailureOrTimeout() {
|
void recoveryPasswordManagerVerificationFailureOrTimeout() {
|
||||||
|
when(phoneNumberIdentifiers.getPhoneNumberIdentifier(any()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(UUID.randomUUID()));
|
||||||
when(registrationRecoveryChecker.checkRegistrationRecoveryAttempt(any(), any())).thenReturn(true);
|
when(registrationRecoveryChecker.checkRegistrationRecoveryAttempt(any(), any())).thenReturn(true);
|
||||||
when(registrationRecoveryPasswordsManager.verify(any(), any()))
|
when(registrationRecoveryPasswordsManager.verify(any(), any()))
|
||||||
.thenReturn(CompletableFuture.failedFuture(new RuntimeException()));
|
.thenReturn(CompletableFuture.failedFuture(new RuntimeException()));
|
||||||
|
@ -289,6 +294,8 @@ class RegistrationControllerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void recoveryPasswordManagerVerificationTrue() throws InterruptedException {
|
void recoveryPasswordManagerVerificationTrue() throws InterruptedException {
|
||||||
|
when(phoneNumberIdentifiers.getPhoneNumberIdentifier(any()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(UUID.randomUUID()));
|
||||||
when(registrationRecoveryChecker.checkRegistrationRecoveryAttempt(any(), any())).thenReturn(true);
|
when(registrationRecoveryChecker.checkRegistrationRecoveryAttempt(any(), any())).thenReturn(true);
|
||||||
when(registrationRecoveryPasswordsManager.verify(any(), any()))
|
when(registrationRecoveryPasswordsManager.verify(any(), any()))
|
||||||
.thenReturn(CompletableFuture.completedFuture(true));
|
.thenReturn(CompletableFuture.completedFuture(true));
|
||||||
|
@ -325,6 +332,8 @@ class RegistrationControllerTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void registrationRecoveryCheckerAllowsAttempt() throws InterruptedException {
|
void registrationRecoveryCheckerAllowsAttempt() throws InterruptedException {
|
||||||
|
when(phoneNumberIdentifiers.getPhoneNumberIdentifier(any()))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(UUID.randomUUID()));
|
||||||
when(registrationRecoveryChecker.checkRegistrationRecoveryAttempt(any(), any())).thenReturn(true);
|
when(registrationRecoveryChecker.checkRegistrationRecoveryAttempt(any(), any())).thenReturn(true);
|
||||||
when(registrationRecoveryPasswordsManager.verify(any(), any()))
|
when(registrationRecoveryPasswordsManager.verify(any(), any()))
|
||||||
.thenReturn(CompletableFuture.completedFuture(true));
|
.thenReturn(CompletableFuture.completedFuture(true));
|
||||||
|
|
|
@ -39,6 +39,7 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
import java.util.concurrent.CompletionException;
|
import java.util.concurrent.CompletionException;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
@ -77,6 +78,7 @@ import org.whispersystems.textsecuregcm.spam.RegistrationFraudChecker;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.PhoneNumberIdentifiers;
|
||||||
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager;
|
||||||
import org.whispersystems.textsecuregcm.storage.VerificationSessionManager;
|
import org.whispersystems.textsecuregcm.storage.VerificationSessionManager;
|
||||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||||
|
@ -87,7 +89,11 @@ class VerificationControllerTest {
|
||||||
private static final long SESSION_EXPIRATION_SECONDS = Duration.ofMinutes(10).toSeconds();
|
private static final long SESSION_EXPIRATION_SECONDS = Duration.ofMinutes(10).toSeconds();
|
||||||
|
|
||||||
private static final byte[] SESSION_ID = "session".getBytes(StandardCharsets.UTF_8);
|
private static final byte[] SESSION_ID = "session".getBytes(StandardCharsets.UTF_8);
|
||||||
private static final String NUMBER = "+18005551212";
|
private static final String NUMBER = PhoneNumberUtil.getInstance().format(
|
||||||
|
PhoneNumberUtil.getInstance().getExampleNumber("US"),
|
||||||
|
PhoneNumberUtil.PhoneNumberFormat.E164);
|
||||||
|
|
||||||
|
private static final UUID PNI = UUID.randomUUID();
|
||||||
|
|
||||||
private final RegistrationServiceClient registrationServiceClient = mock(RegistrationServiceClient.class);
|
private final RegistrationServiceClient registrationServiceClient = mock(RegistrationServiceClient.class);
|
||||||
private final VerificationSessionManager verificationSessionManager = mock(VerificationSessionManager.class);
|
private final VerificationSessionManager verificationSessionManager = mock(VerificationSessionManager.class);
|
||||||
|
@ -95,6 +101,7 @@ class VerificationControllerTest {
|
||||||
private final RegistrationCaptchaManager registrationCaptchaManager = mock(RegistrationCaptchaManager.class);
|
private final RegistrationCaptchaManager registrationCaptchaManager = mock(RegistrationCaptchaManager.class);
|
||||||
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager = mock(
|
private final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager = mock(
|
||||||
RegistrationRecoveryPasswordsManager.class);
|
RegistrationRecoveryPasswordsManager.class);
|
||||||
|
private final PhoneNumberIdentifiers phoneNumberIdentifiers = mock(PhoneNumberIdentifiers.class);
|
||||||
private final RateLimiters rateLimiters = mock(RateLimiters.class);
|
private final RateLimiters rateLimiters = mock(RateLimiters.class);
|
||||||
private final AccountsManager accountsManager = mock(AccountsManager.class);
|
private final AccountsManager accountsManager = mock(AccountsManager.class);
|
||||||
private final Clock clock = Clock.systemUTC();
|
private final Clock clock = Clock.systemUTC();
|
||||||
|
@ -115,7 +122,7 @@ class VerificationControllerTest {
|
||||||
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
||||||
.addResource(
|
.addResource(
|
||||||
new VerificationController(registrationServiceClient, verificationSessionManager, pushNotificationManager,
|
new VerificationController(registrationServiceClient, verificationSessionManager, pushNotificationManager,
|
||||||
registrationCaptchaManager, registrationRecoveryPasswordsManager, rateLimiters, accountsManager,
|
registrationCaptchaManager, registrationRecoveryPasswordsManager, phoneNumberIdentifiers, rateLimiters, accountsManager,
|
||||||
RegistrationFraudChecker.noop(), dynamicConfigurationManager, clock))
|
RegistrationFraudChecker.noop(), dynamicConfigurationManager, clock))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -131,6 +138,8 @@ class VerificationControllerTest {
|
||||||
.thenReturn(new DynamicRegistrationConfiguration(false));
|
.thenReturn(new DynamicRegistrationConfiguration(false));
|
||||||
when(dynamicConfigurationManager.getConfiguration())
|
when(dynamicConfigurationManager.getConfiguration())
|
||||||
.thenReturn(dynamicConfiguration);
|
.thenReturn(dynamicConfiguration);
|
||||||
|
when(phoneNumberIdentifiers.getPhoneNumberIdentifier(NUMBER))
|
||||||
|
.thenReturn(CompletableFuture.completedFuture(PNI));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
|
|
|
@ -65,6 +65,7 @@ import org.whispersystems.textsecuregcm.controllers.AccountController;
|
||||||
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
||||||
import org.whispersystems.textsecuregcm.entities.EncryptedUsername;
|
import org.whispersystems.textsecuregcm.entities.EncryptedUsername;
|
||||||
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
|
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
|
||||||
|
import org.whispersystems.textsecuregcm.identity.IdentityType;
|
||||||
import org.whispersystems.textsecuregcm.identity.PniServiceIdentifier;
|
import org.whispersystems.textsecuregcm.identity.PniServiceIdentifier;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||||
|
@ -113,7 +114,7 @@ class AccountsGrpcServiceTest extends SimpleBaseGrpcTest<AccountsGrpcService, Ac
|
||||||
when(rateLimiter.validateReactive(any(UUID.class))).thenReturn(Mono.empty());
|
when(rateLimiter.validateReactive(any(UUID.class))).thenReturn(Mono.empty());
|
||||||
when(rateLimiter.validateReactive(anyString())).thenReturn(Mono.empty());
|
when(rateLimiter.validateReactive(anyString())).thenReturn(Mono.empty());
|
||||||
|
|
||||||
when(registrationRecoveryPasswordsManager.storeForCurrentNumber(anyString(), any()))
|
when(registrationRecoveryPasswordsManager.storeForCurrentNumber(any(), any()))
|
||||||
.thenReturn(CompletableFuture.completedFuture(null));
|
.thenReturn(CompletableFuture.completedFuture(null));
|
||||||
|
|
||||||
return new AccountsGrpcService(accountsManager,
|
return new AccountsGrpcService(accountsManager,
|
||||||
|
@ -261,7 +262,7 @@ class AccountsGrpcServiceTest extends SimpleBaseGrpcTest<AccountsGrpcService, Ac
|
||||||
final List<byte[]> usernameHashes = invocation.getArgument(1);
|
final List<byte[]> usernameHashes = invocation.getArgument(1);
|
||||||
|
|
||||||
return CompletableFuture.completedFuture(
|
return CompletableFuture.completedFuture(
|
||||||
new AccountsManager.UsernameReservation(invocation.getArgument(0), usernameHashes.get(0)));
|
new AccountsManager.UsernameReservation(invocation.getArgument(0), usernameHashes.getFirst()));
|
||||||
});
|
});
|
||||||
|
|
||||||
final ReserveUsernameHashResponse expectedResponse = ReserveUsernameHashResponse.newBuilder()
|
final ReserveUsernameHashResponse expectedResponse = ReserveUsernameHashResponse.newBuilder()
|
||||||
|
@ -684,12 +685,10 @@ class AccountsGrpcServiceTest extends SimpleBaseGrpcTest<AccountsGrpcService, Ac
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void setRegistrationRecoveryPassword() {
|
void setRegistrationRecoveryPassword() {
|
||||||
final String phoneNumber =
|
final UUID phoneNumberIdentifier = UUID.randomUUID();
|
||||||
PhoneNumberUtil.getInstance().format(PhoneNumberUtil.getInstance().getExampleNumber("US"),
|
|
||||||
PhoneNumberUtil.PhoneNumberFormat.E164);
|
|
||||||
|
|
||||||
final Account account = mock(Account.class);
|
final Account account = mock(Account.class);
|
||||||
when(account.getNumber()).thenReturn(phoneNumber);
|
when(account.getIdentifier(IdentityType.PNI)).thenReturn(phoneNumberIdentifier);
|
||||||
|
|
||||||
when(accountsManager.getByAccountIdentifierAsync(AUTHENTICATED_ACI))
|
when(accountsManager.getByAccountIdentifierAsync(AUTHENTICATED_ACI))
|
||||||
.thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
.thenReturn(CompletableFuture.completedFuture(Optional.of(account)));
|
||||||
|
@ -701,7 +700,7 @@ class AccountsGrpcServiceTest extends SimpleBaseGrpcTest<AccountsGrpcService, Ac
|
||||||
.setRegistrationRecoveryPassword(ByteString.copyFrom(registrationRecoveryPassword))
|
.setRegistrationRecoveryPassword(ByteString.copyFrom(registrationRecoveryPassword))
|
||||||
.build()));
|
.build()));
|
||||||
|
|
||||||
verify(registrationRecoveryPasswordsManager).storeForCurrentNumber(phoneNumber, registrationRecoveryPassword);
|
verify(registrationRecoveryPasswordsManager).storeForCurrentNumber(account.getNumber(), registrationRecoveryPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -224,7 +224,7 @@ class AccountsManagerTest {
|
||||||
final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager =
|
final RegistrationRecoveryPasswordsManager registrationRecoveryPasswordsManager =
|
||||||
mock(RegistrationRecoveryPasswordsManager.class);
|
mock(RegistrationRecoveryPasswordsManager.class);
|
||||||
|
|
||||||
when(registrationRecoveryPasswordsManager.removeForNumber(anyString())).thenReturn(CompletableFuture.completedFuture(null));
|
when(registrationRecoveryPasswordsManager.removeForNumber(any())).thenReturn(CompletableFuture.completedFuture(null));
|
||||||
|
|
||||||
when(keysManager.deleteSingleUsePreKeys(any())).thenReturn(CompletableFuture.completedFuture(null));
|
when(keysManager.deleteSingleUsePreKeys(any())).thenReturn(CompletableFuture.completedFuture(null));
|
||||||
when(messagesManager.clear(any())).thenReturn(CompletableFuture.completedFuture(null));
|
when(messagesManager.clear(any())).thenReturn(CompletableFuture.completedFuture(null));
|
||||||
|
|
|
@ -288,10 +288,10 @@ public final class DynamoDbExtensionSchema {
|
||||||
List.of(), List.of()),
|
List.of(), List.of()),
|
||||||
|
|
||||||
REGISTRATION_RECOVERY_PASSWORDS("registration_recovery_passwords_test",
|
REGISTRATION_RECOVERY_PASSWORDS("registration_recovery_passwords_test",
|
||||||
RegistrationRecoveryPasswords.KEY_E164,
|
RegistrationRecoveryPasswords.KEY_PNI,
|
||||||
null,
|
null,
|
||||||
List.of(AttributeDefinition.builder()
|
List.of(AttributeDefinition.builder()
|
||||||
.attributeName(RegistrationRecoveryPasswords.KEY_E164)
|
.attributeName(RegistrationRecoveryPasswords.KEY_PNI)
|
||||||
.attributeType(ScalarAttributeType.S)
|
.attributeType(ScalarAttributeType.S)
|
||||||
.build()),
|
.build()),
|
||||||
List.of(), List.of()),
|
List.of(), List.of()),
|
||||||
|
|
|
@ -32,20 +32,17 @@ import software.amazon.awssdk.services.dynamodb.model.GetItemRequest;
|
||||||
public class RegistrationRecoveryTest {
|
public class RegistrationRecoveryTest {
|
||||||
|
|
||||||
private static final MutableClock CLOCK = MockUtils.mutableClock(0);
|
private static final MutableClock CLOCK = MockUtils.mutableClock(0);
|
||||||
|
|
||||||
private static final Duration EXPIRATION = Duration.ofSeconds(1000);
|
private static final Duration EXPIRATION = Duration.ofSeconds(1000);
|
||||||
|
|
||||||
private static final String NUMBER = "+18005555555";
|
private static final String NUMBER = "+18005555555";
|
||||||
private static final UUID PNI = UUID.randomUUID();
|
|
||||||
|
|
||||||
private static final SaltedTokenHash ORIGINAL_HASH = SaltedTokenHash.generateFor("pass1");
|
private static final SaltedTokenHash ORIGINAL_HASH = SaltedTokenHash.generateFor("pass1");
|
||||||
|
|
||||||
private static final SaltedTokenHash ANOTHER_HASH = SaltedTokenHash.generateFor("pass2");
|
private static final SaltedTokenHash ANOTHER_HASH = SaltedTokenHash.generateFor("pass2");
|
||||||
|
|
||||||
@RegisterExtension
|
@RegisterExtension
|
||||||
private static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(
|
private static final DynamoDbExtension DYNAMO_DB_EXTENSION =
|
||||||
Tables.PNI,
|
new DynamoDbExtension(Tables.PNI, Tables.REGISTRATION_RECOVERY_PASSWORDS);
|
||||||
Tables.REGISTRATION_RECOVERY_PASSWORDS);
|
|
||||||
|
private UUID pni;
|
||||||
|
|
||||||
private RegistrationRecoveryPasswords registrationRecoveryPasswords;
|
private RegistrationRecoveryPasswords registrationRecoveryPasswords;
|
||||||
|
|
||||||
|
@ -60,89 +57,63 @@ public class RegistrationRecoveryTest {
|
||||||
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
|
DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(),
|
||||||
CLOCK
|
CLOCK
|
||||||
);
|
);
|
||||||
|
|
||||||
final PhoneNumberIdentifiers phoneNumberIdentifiers =
|
final PhoneNumberIdentifiers phoneNumberIdentifiers =
|
||||||
new PhoneNumberIdentifiers(DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(), Tables.PNI.tableName());
|
new PhoneNumberIdentifiers(DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient(), Tables.PNI.tableName());
|
||||||
|
|
||||||
manager = new RegistrationRecoveryPasswordsManager(registrationRecoveryPasswords, phoneNumberIdentifiers);
|
manager = new RegistrationRecoveryPasswordsManager(registrationRecoveryPasswords, phoneNumberIdentifiers);
|
||||||
|
|
||||||
|
pni = phoneNumberIdentifiers.getPhoneNumberIdentifier(NUMBER).join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLookupAfterWrite() throws Exception {
|
public void testLookupAfterWrite() throws Exception {
|
||||||
registrationRecoveryPasswords.addOrReplace(NUMBER, PNI, ORIGINAL_HASH).get();
|
registrationRecoveryPasswords.addOrReplace(NUMBER, pni, ORIGINAL_HASH).get();
|
||||||
final long initialExp = fetchTimestamp(NUMBER);
|
final long initialExp = fetchTimestamp(NUMBER);
|
||||||
final long expectedExpiration = CLOCK.instant().getEpochSecond() + EXPIRATION.getSeconds();
|
final long expectedExpiration = CLOCK.instant().getEpochSecond() + EXPIRATION.getSeconds();
|
||||||
assertEquals(expectedExpiration, initialExp);
|
assertEquals(expectedExpiration, initialExp);
|
||||||
|
|
||||||
{
|
final Optional<SaltedTokenHash> saltedTokenHashByPni = registrationRecoveryPasswords.lookup(pni).get();
|
||||||
final Optional<SaltedTokenHash> saltedTokenHashByNumber = registrationRecoveryPasswords.lookup(NUMBER).get();
|
assertTrue(saltedTokenHashByPni.isPresent());
|
||||||
assertTrue(saltedTokenHashByNumber.isPresent());
|
assertEquals(ORIGINAL_HASH.salt(), saltedTokenHashByPni.get().salt());
|
||||||
assertEquals(ORIGINAL_HASH.salt(), saltedTokenHashByNumber.get().salt());
|
assertEquals(ORIGINAL_HASH.hash(), saltedTokenHashByPni.get().hash());
|
||||||
assertEquals(ORIGINAL_HASH.hash(), saltedTokenHashByNumber.get().hash());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
final Optional<SaltedTokenHash> saltedTokenHashByPni = registrationRecoveryPasswords.lookup(PNI).get();
|
|
||||||
assertTrue(saltedTokenHashByPni.isPresent());
|
|
||||||
assertEquals(ORIGINAL_HASH.salt(), saltedTokenHashByPni.get().salt());
|
|
||||||
assertEquals(ORIGINAL_HASH.hash(), saltedTokenHashByPni.get().hash());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testLookupAfterRefresh() throws Exception {
|
public void testLookupAfterRefresh() throws Exception {
|
||||||
registrationRecoveryPasswords.addOrReplace(NUMBER, PNI, ORIGINAL_HASH).get();
|
registrationRecoveryPasswords.addOrReplace(NUMBER, pni, ORIGINAL_HASH).get();
|
||||||
|
|
||||||
CLOCK.increment(50, TimeUnit.SECONDS);
|
CLOCK.increment(50, TimeUnit.SECONDS);
|
||||||
registrationRecoveryPasswords.addOrReplace(NUMBER, PNI, ORIGINAL_HASH).get();
|
registrationRecoveryPasswords.addOrReplace(NUMBER, pni, ORIGINAL_HASH).get();
|
||||||
final long updatedExp = fetchTimestamp(NUMBER);
|
final long updatedExp = fetchTimestamp(NUMBER);
|
||||||
final long expectedExp = CLOCK.instant().getEpochSecond() + EXPIRATION.getSeconds();
|
final long expectedExp = CLOCK.instant().getEpochSecond() + EXPIRATION.getSeconds();
|
||||||
assertEquals(expectedExp, updatedExp);
|
assertEquals(expectedExp, updatedExp);
|
||||||
|
|
||||||
{
|
final Optional<SaltedTokenHash> saltedTokenHashByPni = registrationRecoveryPasswords.lookup(pni).get();
|
||||||
final Optional<SaltedTokenHash> saltedTokenHashByNumber = registrationRecoveryPasswords.lookup(NUMBER).get();
|
assertTrue(saltedTokenHashByPni.isPresent());
|
||||||
assertTrue(saltedTokenHashByNumber.isPresent());
|
assertEquals(ORIGINAL_HASH.salt(), saltedTokenHashByPni.get().salt());
|
||||||
assertEquals(ORIGINAL_HASH.salt(), saltedTokenHashByNumber.get().salt());
|
assertEquals(ORIGINAL_HASH.hash(), saltedTokenHashByPni.get().hash());
|
||||||
assertEquals(ORIGINAL_HASH.hash(), saltedTokenHashByNumber.get().hash());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
final Optional<SaltedTokenHash> saltedTokenHashByPni = registrationRecoveryPasswords.lookup(PNI).get();
|
|
||||||
assertTrue(saltedTokenHashByPni.isPresent());
|
|
||||||
assertEquals(ORIGINAL_HASH.salt(), saltedTokenHashByPni.get().salt());
|
|
||||||
assertEquals(ORIGINAL_HASH.hash(), saltedTokenHashByPni.get().hash());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testReplace() throws Exception {
|
public void testReplace() throws Exception {
|
||||||
registrationRecoveryPasswords.addOrReplace(NUMBER, PNI, ORIGINAL_HASH).get();
|
registrationRecoveryPasswords.addOrReplace(NUMBER, pni, ORIGINAL_HASH).get();
|
||||||
registrationRecoveryPasswords.addOrReplace(NUMBER, PNI, ANOTHER_HASH).get();
|
registrationRecoveryPasswords.addOrReplace(NUMBER, pni, ANOTHER_HASH).get();
|
||||||
|
|
||||||
{
|
final Optional<SaltedTokenHash> saltedTokenHashByPni = registrationRecoveryPasswords.lookup(pni).get();
|
||||||
final Optional<SaltedTokenHash> saltedTokenHashByNumber = registrationRecoveryPasswords.lookup(NUMBER).get();
|
assertTrue(saltedTokenHashByPni.isPresent());
|
||||||
assertTrue(saltedTokenHashByNumber.isPresent());
|
assertEquals(ANOTHER_HASH.salt(), saltedTokenHashByPni.get().salt());
|
||||||
assertEquals(ANOTHER_HASH.salt(), saltedTokenHashByNumber.get().salt());
|
assertEquals(ANOTHER_HASH.hash(), saltedTokenHashByPni.get().hash());
|
||||||
assertEquals(ANOTHER_HASH.hash(), saltedTokenHashByNumber.get().hash());
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
final Optional<SaltedTokenHash> saltedTokenHashByPni = registrationRecoveryPasswords.lookup(PNI).get();
|
|
||||||
assertTrue(saltedTokenHashByPni.isPresent());
|
|
||||||
assertEquals(ANOTHER_HASH.salt(), saltedTokenHashByPni.get().salt());
|
|
||||||
assertEquals(ANOTHER_HASH.hash(), saltedTokenHashByPni.get().hash());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemove() throws Exception {
|
public void testRemove() throws Exception {
|
||||||
assertDoesNotThrow(() -> registrationRecoveryPasswords.removeEntry(NUMBER, PNI).join());
|
assertDoesNotThrow(() -> registrationRecoveryPasswords.removeEntry(NUMBER, pni).join());
|
||||||
|
|
||||||
registrationRecoveryPasswords.addOrReplace(NUMBER, PNI, ORIGINAL_HASH).get();
|
registrationRecoveryPasswords.addOrReplace(NUMBER, pni, ORIGINAL_HASH).get();
|
||||||
assertTrue(registrationRecoveryPasswords.lookup(NUMBER).get().isPresent());
|
assertTrue(registrationRecoveryPasswords.lookup(pni).get().isPresent());
|
||||||
|
|
||||||
registrationRecoveryPasswords.removeEntry(NUMBER, PNI).get();
|
registrationRecoveryPasswords.removeEntry(NUMBER, pni).get();
|
||||||
assertTrue(registrationRecoveryPasswords.lookup(NUMBER).get().isEmpty());
|
assertTrue(registrationRecoveryPasswords.lookup(pni).get().isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -153,30 +124,30 @@ public class RegistrationRecoveryTest {
|
||||||
|
|
||||||
// initial store
|
// initial store
|
||||||
manager.storeForCurrentNumber(NUMBER, password).get();
|
manager.storeForCurrentNumber(NUMBER, password).get();
|
||||||
assertTrue(manager.verify(NUMBER, password).get());
|
assertTrue(manager.verify(pni, password).get());
|
||||||
assertFalse(manager.verify(NUMBER, wrongPassword).get());
|
assertFalse(manager.verify(pni, wrongPassword).get());
|
||||||
|
|
||||||
// update
|
// update
|
||||||
manager.storeForCurrentNumber(NUMBER, password).get();
|
manager.storeForCurrentNumber(NUMBER, password).get();
|
||||||
assertTrue(manager.verify(NUMBER, password).get());
|
assertTrue(manager.verify(pni, password).get());
|
||||||
assertFalse(manager.verify(NUMBER, wrongPassword).get());
|
assertFalse(manager.verify(pni, wrongPassword).get());
|
||||||
|
|
||||||
// replace
|
// replace
|
||||||
manager.storeForCurrentNumber(NUMBER, updatedPassword).get();
|
manager.storeForCurrentNumber(NUMBER, updatedPassword).get();
|
||||||
assertTrue(manager.verify(NUMBER, updatedPassword).get());
|
assertTrue(manager.verify(pni, updatedPassword).get());
|
||||||
assertFalse(manager.verify(NUMBER, password).get());
|
assertFalse(manager.verify(pni, password).get());
|
||||||
assertFalse(manager.verify(NUMBER, wrongPassword).get());
|
assertFalse(manager.verify(pni, wrongPassword).get());
|
||||||
|
|
||||||
manager.removeForNumber(NUMBER).get();
|
manager.removeForNumber(NUMBER).get();
|
||||||
assertFalse(manager.verify(NUMBER, updatedPassword).get());
|
assertFalse(manager.verify(pni, updatedPassword).get());
|
||||||
assertFalse(manager.verify(NUMBER, password).get());
|
assertFalse(manager.verify(pni, password).get());
|
||||||
assertFalse(manager.verify(NUMBER, wrongPassword).get());
|
assertFalse(manager.verify(pni, wrongPassword).get());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long fetchTimestamp(final String number) throws ExecutionException, InterruptedException {
|
private static long fetchTimestamp(final String number) throws ExecutionException, InterruptedException {
|
||||||
return DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient().getItem(GetItemRequest.builder()
|
return DYNAMO_DB_EXTENSION.getDynamoDbAsyncClient().getItem(GetItemRequest.builder()
|
||||||
.tableName(Tables.REGISTRATION_RECOVERY_PASSWORDS.tableName())
|
.tableName(Tables.REGISTRATION_RECOVERY_PASSWORDS.tableName())
|
||||||
.key(Map.of(RegistrationRecoveryPasswords.KEY_E164, AttributeValues.fromString(number)))
|
.key(Map.of(RegistrationRecoveryPasswords.KEY_PNI, AttributeValues.fromString(number)))
|
||||||
.build())
|
.build())
|
||||||
.thenApply(getItemResponse -> {
|
.thenApply(getItemResponse -> {
|
||||||
final Map<String, AttributeValue> item = getItemResponse.item();
|
final Map<String, AttributeValue> item = getItemResponse.item();
|
||||||
|
|
|
@ -85,6 +85,7 @@ public class AuthHelper {
|
||||||
|
|
||||||
public static final String UNDISCOVERABLE_NUMBER = "+18005551234";
|
public static final String UNDISCOVERABLE_NUMBER = "+18005551234";
|
||||||
public static final UUID UNDISCOVERABLE_UUID = UUID.randomUUID();
|
public static final UUID UNDISCOVERABLE_UUID = UUID.randomUUID();
|
||||||
|
public static final UUID UNDISCOVERABLE_PNI = UUID.randomUUID();
|
||||||
public static final String UNDISCOVERABLE_PASSWORD = "IT'S A SECRET TO EVERYBODY.";
|
public static final String UNDISCOVERABLE_PASSWORD = "IT'S A SECRET TO EVERYBODY.";
|
||||||
|
|
||||||
public static final ECKeyPair VALID_IDENTITY_KEY_PAIR = Curve.generateKeyPair();
|
public static final ECKeyPair VALID_IDENTITY_KEY_PAIR = Curve.generateKeyPair();
|
||||||
|
@ -169,7 +170,9 @@ public class AuthHelper {
|
||||||
when(VALID_ACCOUNT_TWO.getPhoneNumberIdentifier()).thenReturn(VALID_PNI_TWO);
|
when(VALID_ACCOUNT_TWO.getPhoneNumberIdentifier()).thenReturn(VALID_PNI_TWO);
|
||||||
when(UNDISCOVERABLE_ACCOUNT.getNumber()).thenReturn(UNDISCOVERABLE_NUMBER);
|
when(UNDISCOVERABLE_ACCOUNT.getNumber()).thenReturn(UNDISCOVERABLE_NUMBER);
|
||||||
when(UNDISCOVERABLE_ACCOUNT.getUuid()).thenReturn(UNDISCOVERABLE_UUID);
|
when(UNDISCOVERABLE_ACCOUNT.getUuid()).thenReturn(UNDISCOVERABLE_UUID);
|
||||||
|
when(UNDISCOVERABLE_ACCOUNT.getPhoneNumberIdentifier()).thenReturn(UNDISCOVERABLE_PNI);
|
||||||
when(UNDISCOVERABLE_ACCOUNT.getIdentifier(IdentityType.ACI)).thenReturn(UNDISCOVERABLE_UUID);
|
when(UNDISCOVERABLE_ACCOUNT.getIdentifier(IdentityType.ACI)).thenReturn(UNDISCOVERABLE_UUID);
|
||||||
|
when(UNDISCOVERABLE_ACCOUNT.getIdentifier(IdentityType.PNI)).thenReturn(UNDISCOVERABLE_PNI);
|
||||||
when(VALID_ACCOUNT_3.getNumber()).thenReturn(VALID_NUMBER_3);
|
when(VALID_ACCOUNT_3.getNumber()).thenReturn(VALID_NUMBER_3);
|
||||||
when(VALID_ACCOUNT_3.getUuid()).thenReturn(VALID_UUID_3);
|
when(VALID_ACCOUNT_3.getUuid()).thenReturn(VALID_UUID_3);
|
||||||
when(VALID_ACCOUNT_3.getPhoneNumberIdentifier()).thenReturn(VALID_PNI_3);
|
when(VALID_ACCOUNT_3.getPhoneNumberIdentifier()).thenReturn(VALID_PNI_3);
|
||||||
|
|
Loading…
Reference in New Issue