Add storage capability and return KBS creds on rereg w/ storage set
This commit is contained in:
parent
bb7433ab40
commit
3c8e7c6c10
|
@ -0,0 +1,59 @@
|
||||||
|
package org.whispersystems.textsecuregcm.auth;
|
||||||
|
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import org.whispersystems.textsecuregcm.util.Util;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||||
|
public class StoredRegistrationLock {
|
||||||
|
|
||||||
|
private final Optional<String> registrationLock;
|
||||||
|
|
||||||
|
private final Optional<String> registrationLockSalt;
|
||||||
|
|
||||||
|
private final Optional<String> deprecatedPin;
|
||||||
|
|
||||||
|
private final long lastSeen;
|
||||||
|
|
||||||
|
public StoredRegistrationLock(Optional<String> registrationLock, Optional<String> registrationLockSalt, Optional<String> deprecatedPin, long lastSeen) {
|
||||||
|
this.registrationLock = registrationLock;
|
||||||
|
this.registrationLockSalt = registrationLockSalt;
|
||||||
|
this.deprecatedPin = deprecatedPin;
|
||||||
|
this.lastSeen = lastSeen;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean requiresClientRegistrationLock() {
|
||||||
|
return ((registrationLock.isPresent() && registrationLockSalt.isPresent()) || deprecatedPin.isPresent()) && System.currentTimeMillis() - lastSeen < TimeUnit.DAYS.toMillis(7);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean needsFailureCredentials() {
|
||||||
|
return registrationLock.isPresent() && registrationLockSalt.isPresent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimeRemaining() {
|
||||||
|
return TimeUnit.DAYS.toMillis(7) - (System.currentTimeMillis() - lastSeen);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean verify(@Nullable String clientRegistrationLock, @Nullable String clientDeprecatedPin) {
|
||||||
|
if (Util.isEmpty(clientRegistrationLock) && Util.isEmpty(clientDeprecatedPin)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (registrationLock.isPresent() && registrationLockSalt.isPresent() && !Util.isEmpty(clientRegistrationLock)) {
|
||||||
|
return new AuthenticationCredentials(registrationLock.get(), registrationLockSalt.get()).verify(clientRegistrationLock);
|
||||||
|
} else if (deprecatedPin.isPresent() && !Util.isEmpty(clientDeprecatedPin)) {
|
||||||
|
return MessageDigest.isEqual(deprecatedPin.get().getBytes(), clientDeprecatedPin.getBytes());
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public StoredRegistrationLock forTime(long timestamp) {
|
||||||
|
return new StoredRegistrationLock(registrationLock, registrationLockSalt, deprecatedPin, timestamp);
|
||||||
|
}
|
||||||
|
}
|
|
@ -29,6 +29,7 @@ import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||||
import org.whispersystems.textsecuregcm.auth.InvalidAuthorizationHeaderException;
|
import org.whispersystems.textsecuregcm.auth.InvalidAuthorizationHeaderException;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
|
||||||
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
||||||
import org.whispersystems.textsecuregcm.auth.TurnToken;
|
import org.whispersystems.textsecuregcm.auth.TurnToken;
|
||||||
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
||||||
|
@ -74,14 +75,12 @@ import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.WebApplicationException;
|
import javax.ws.rs.WebApplicationException;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
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.UUID;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import static com.codahale.metrics.MetricRegistry.name;
|
import static com.codahale.metrics.MetricRegistry.name;
|
||||||
import io.dropwizard.auth.Auth;
|
import io.dropwizard.auth.Auth;
|
||||||
|
@ -270,48 +269,26 @@ public class AccountController {
|
||||||
|
|
||||||
Optional<StoredVerificationCode> storedVerificationCode = pendingAccounts.getCodeForNumber(number);
|
Optional<StoredVerificationCode> storedVerificationCode = pendingAccounts.getCodeForNumber(number);
|
||||||
|
|
||||||
if (!storedVerificationCode.isPresent() || !storedVerificationCode.get().isValid(verificationCode)) {
|
if (storedVerificationCode.isEmpty() || !storedVerificationCode.get().isValid(verificationCode)) {
|
||||||
throw new WebApplicationException(Response.status(403).build());
|
throw new WebApplicationException(Response.status(403).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
Optional<Account> existingAccount = accounts.get(number);
|
Optional<Account> existingAccount = accounts.get(number);
|
||||||
|
Optional<StoredRegistrationLock> existingRegistrationLock = existingAccount.map(Account::getRegistrationLock);
|
||||||
|
Optional<ExternalServiceCredentials> existingBackupCredentials = existingAccount.map(Account::getUuid)
|
||||||
|
.map(uuid -> backupServiceCredentialGenerator.generateFor(uuid.toString()));
|
||||||
|
|
||||||
if (existingAccount.isPresent() &&
|
if (existingRegistrationLock.isPresent() && existingRegistrationLock.get().requiresClientRegistrationLock()) {
|
||||||
(existingAccount.get().getPin().isPresent() || existingAccount.get().getRegistrationLock().isPresent()) &&
|
|
||||||
System.currentTimeMillis() - existingAccount.get().getLastSeen() < TimeUnit.DAYS.toMillis(7))
|
|
||||||
{
|
|
||||||
rateLimiters.getVerifyLimiter().clear(number);
|
rateLimiters.getVerifyLimiter().clear(number);
|
||||||
|
|
||||||
long timeRemaining = TimeUnit.DAYS.toMillis(7) - (System.currentTimeMillis() - existingAccount.get().getLastSeen());
|
if (!Util.isEmpty(accountAttributes.getRegistrationLock()) || !Util.isEmpty(accountAttributes.getPin())) {
|
||||||
Optional<ExternalServiceCredentials> credentials = existingAccount.get().getRegistrationLock().isPresent() &&
|
rateLimiters.getPinLimiter().validate(number);
|
||||||
existingAccount.get().getRegistrationLockSalt().isPresent() ?
|
|
||||||
Optional.of(backupServiceCredentialGenerator.generateFor(existingAccount.get().getUuid().toString())) :
|
|
||||||
Optional.empty();
|
|
||||||
|
|
||||||
if (Util.isEmpty(accountAttributes.getPin()) &&
|
|
||||||
Util.isEmpty(accountAttributes.getRegistrationLock()))
|
|
||||||
{
|
|
||||||
throw new WebApplicationException(Response.status(423)
|
|
||||||
.entity(new RegistrationLockFailure(timeRemaining, credentials.orElse(null)))
|
|
||||||
.build());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rateLimiters.getPinLimiter().validate(number);
|
if (!existingRegistrationLock.get().verify(accountAttributes.getRegistrationLock(), accountAttributes.getPin())) {
|
||||||
|
|
||||||
boolean pinMatches;
|
|
||||||
|
|
||||||
if (existingAccount.get().getRegistrationLock().isPresent() && existingAccount.get().getRegistrationLockSalt().isPresent()) {
|
|
||||||
pinMatches = new AuthenticationCredentials(existingAccount.get().getRegistrationLock().get(),
|
|
||||||
existingAccount.get().getRegistrationLockSalt().get()).verify(accountAttributes.getRegistrationLock());
|
|
||||||
} else if (existingAccount.get().getPin().isPresent()) {
|
|
||||||
pinMatches = MessageDigest.isEqual(existingAccount.get().getPin().get().getBytes(), accountAttributes.getPin().getBytes());
|
|
||||||
} else {
|
|
||||||
throw new AssertionError("Invalid registration lock state");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pinMatches) {
|
|
||||||
throw new WebApplicationException(Response.status(423)
|
throw new WebApplicationException(Response.status(423)
|
||||||
.entity(new RegistrationLockFailure(timeRemaining, credentials.orElse(null)))
|
.entity(new RegistrationLockFailure(existingRegistrationLock.get().getTimeRemaining(),
|
||||||
|
existingRegistrationLock.get().needsFailureCredentials() ? existingBackupCredentials.orElseThrow() : null))
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,7 +299,7 @@ public class AccountController {
|
||||||
|
|
||||||
metricRegistry.meter(name(AccountController.class, "verify", Util.getCountryCode(number))).mark();
|
metricRegistry.meter(name(AccountController.class, "verify", Util.getCountryCode(number))).mark();
|
||||||
|
|
||||||
return new AccountCreationResult(account.getUuid());
|
return new AccountCreationResult(account.getUuid(), existingAccount.map(Account::isStorageSupported).orElse(false) ? existingBackupCredentials.orElse(null) : null);
|
||||||
} catch (InvalidAuthorizationHeaderException e) {
|
} catch (InvalidAuthorizationHeaderException e) {
|
||||||
logger.info("Bad Authorization Header", e);
|
logger.info("Bad Authorization Header", e);
|
||||||
throw new WebApplicationException(Response.status(401).build());
|
throw new WebApplicationException(Response.status(401).build());
|
||||||
|
@ -423,8 +400,7 @@ public class AccountController {
|
||||||
@Path("/registration_lock")
|
@Path("/registration_lock")
|
||||||
public void setRegistrationLock(@Auth Account account, @Valid RegistrationLock accountLock) {
|
public void setRegistrationLock(@Auth Account account, @Valid RegistrationLock accountLock) {
|
||||||
AuthenticationCredentials credentials = new AuthenticationCredentials(accountLock.getRegistrationLock());
|
AuthenticationCredentials credentials = new AuthenticationCredentials(accountLock.getRegistrationLock());
|
||||||
account.setRegistrationLock(credentials.getHashedAuthenticationToken());
|
account.setRegistrationLock(credentials.getHashedAuthenticationToken(), credentials.getSalt());
|
||||||
account.setRegistrationLockSalt(credentials.getSalt());
|
|
||||||
account.setPin(null);
|
account.setPin(null);
|
||||||
|
|
||||||
accounts.update(account);
|
accounts.update(account);
|
||||||
|
@ -434,8 +410,7 @@ public class AccountController {
|
||||||
@DELETE
|
@DELETE
|
||||||
@Path("/registration_lock")
|
@Path("/registration_lock")
|
||||||
public void removeRegistrationLock(@Auth Account account) {
|
public void removeRegistrationLock(@Auth Account account) {
|
||||||
account.setRegistrationLock(null);
|
account.setRegistrationLock(null, null);
|
||||||
account.setRegistrationLockSalt(null);
|
|
||||||
accounts.update(account);
|
accounts.update(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -445,8 +420,7 @@ public class AccountController {
|
||||||
@Path("/pin/")
|
@Path("/pin/")
|
||||||
public void setPin(@Auth Account account, @Valid DeprecatedPin accountLock) {
|
public void setPin(@Auth Account account, @Valid DeprecatedPin accountLock) {
|
||||||
account.setPin(accountLock.getPin());
|
account.setPin(accountLock.getPin());
|
||||||
account.setRegistrationLock(null);
|
account.setRegistrationLock(null, null);
|
||||||
account.setRegistrationLockSalt(null);
|
|
||||||
|
|
||||||
accounts.update(account);
|
accounts.update(account);
|
||||||
}
|
}
|
||||||
|
@ -500,12 +474,10 @@ public class AccountController {
|
||||||
account.setPin(attributes.getPin());
|
account.setPin(attributes.getPin());
|
||||||
} else if (!Util.isEmpty(attributes.getRegistrationLock())) {
|
} else if (!Util.isEmpty(attributes.getRegistrationLock())) {
|
||||||
AuthenticationCredentials credentials = new AuthenticationCredentials(attributes.getRegistrationLock());
|
AuthenticationCredentials credentials = new AuthenticationCredentials(attributes.getRegistrationLock());
|
||||||
account.setRegistrationLock(credentials.getHashedAuthenticationToken());
|
account.setRegistrationLock(credentials.getHashedAuthenticationToken(), credentials.getSalt());
|
||||||
account.setRegistrationLockSalt(credentials.getSalt());
|
|
||||||
} else {
|
} else {
|
||||||
account.setPin(null);
|
account.setPin(null);
|
||||||
account.setRegistrationLock(null);
|
account.setRegistrationLock(null, null);
|
||||||
account.setRegistrationLockSalt(null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
account.setUnidentifiedAccessKey(attributes.getUnidentifiedAccessKey());
|
account.setUnidentifiedAccessKey(attributes.getUnidentifiedAccessKey());
|
||||||
|
@ -518,7 +490,7 @@ public class AccountController {
|
||||||
@Path("/whoami")
|
@Path("/whoami")
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
public AccountCreationResult whoAmI(@Auth Account account) {
|
public AccountCreationResult whoAmI(@Auth Account account) {
|
||||||
return new AccountCreationResult(account.getUuid());
|
return new AccountCreationResult(account.getUuid(), backupServiceCredentialGenerator.generateFor(account.getUuid().toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@DELETE
|
@DELETE
|
||||||
|
|
|
@ -2,6 +2,8 @@ package org.whispersystems.textsecuregcm.entities;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public class AccountCreationResult {
|
public class AccountCreationResult {
|
||||||
|
@ -9,13 +11,21 @@ public class AccountCreationResult {
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private UUID uuid;
|
private UUID uuid;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private ExternalServiceCredentials backupCredentials;
|
||||||
|
|
||||||
public AccountCreationResult() {}
|
public AccountCreationResult() {}
|
||||||
|
|
||||||
public AccountCreationResult(UUID uuid) {
|
public AccountCreationResult(UUID uuid, ExternalServiceCredentials backupCredentials) {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
|
this.backupCredentials = backupCredentials;
|
||||||
}
|
}
|
||||||
|
|
||||||
public UUID getUuid() {
|
public UUID getUuid() {
|
||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ExternalServiceCredentials getBackupCredentials() {
|
||||||
|
return backupCredentials;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
|
import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
|
||||||
|
|
||||||
import javax.security.auth.Subject;
|
import javax.security.auth.Subject;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
|
@ -144,6 +145,10 @@ public class Account implements Principal {
|
||||||
.allMatch(device -> device.getCapabilities() != null && device.getCapabilities().isGv2());
|
.allMatch(device -> device.getCapabilities() != null && device.getCapabilities().isGv2());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isStorageSupported() {
|
||||||
|
return devices.stream().anyMatch(device -> device.getCapabilities() != null && device.getCapabilities().isStorage());
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return
|
return
|
||||||
getMasterDevice().isPresent() &&
|
getMasterDevice().isPresent() &&
|
||||||
|
@ -227,30 +232,19 @@ public class Account implements Principal {
|
||||||
this.avatarDigest = avatarDigest;
|
this.avatarDigest = avatarDigest;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<String> getPin() {
|
|
||||||
return Optional.ofNullable(pin);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPin(String pin) {
|
public void setPin(String pin) {
|
||||||
this.pin = pin;
|
this.pin = pin;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRegistrationLock(String registrationLock) {
|
public void setRegistrationLock(String registrationLock, String registrationLockSalt) {
|
||||||
this.registrationLock = registrationLock;
|
this.registrationLock = registrationLock;
|
||||||
}
|
|
||||||
|
|
||||||
public Optional<String> getRegistrationLock() {
|
|
||||||
return Optional.ofNullable(registrationLock);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRegistrationLockSalt(String registrationLockSalt) {
|
|
||||||
this.registrationLockSalt = registrationLockSalt;
|
this.registrationLockSalt = registrationLockSalt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<String> getRegistrationLockSalt() {
|
public StoredRegistrationLock getRegistrationLock() {
|
||||||
return Optional.ofNullable(registrationLockSalt);
|
return new StoredRegistrationLock(Optional.ofNullable(registrationLock), Optional.ofNullable(registrationLockSalt), Optional.ofNullable(pin), getLastSeen());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<byte[]> getUnidentifiedAccessKey() {
|
public Optional<byte[]> getUnidentifiedAccessKey() {
|
||||||
return Optional.ofNullable(unidentifiedAccessKey);
|
return Optional.ofNullable(unidentifiedAccessKey);
|
||||||
}
|
}
|
||||||
|
|
|
@ -273,11 +273,15 @@ public class Device {
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private boolean gv2;
|
private boolean gv2;
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private boolean storage;
|
||||||
|
|
||||||
public DeviceCapabilities() {}
|
public DeviceCapabilities() {}
|
||||||
|
|
||||||
public DeviceCapabilities(boolean uuid, boolean gv2) {
|
public DeviceCapabilities(boolean uuid, boolean gv2, boolean storage) {
|
||||||
this.uuid = uuid;
|
this.uuid = uuid;
|
||||||
this.gv2 = gv2;
|
this.gv2 = gv2;
|
||||||
|
this.storage = storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isUuid() {
|
public boolean isUuid() {
|
||||||
|
@ -287,6 +291,10 @@ public class Device {
|
||||||
public boolean isGv2() {
|
public boolean isGv2() {
|
||||||
return gv2;
|
return gv2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isStorage() {
|
||||||
|
return storage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import org.mockito.ArgumentCaptor;
|
||||||
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
|
||||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
|
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
|
||||||
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
||||||
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
||||||
import org.whispersystems.textsecuregcm.controllers.AccountController;
|
import org.whispersystems.textsecuregcm.controllers.AccountController;
|
||||||
|
@ -68,6 +69,7 @@ public class AccountControllerTest {
|
||||||
private static final String SENDER_OVER_PREFIX = "+14156666666";
|
private static final String SENDER_OVER_PREFIX = "+14156666666";
|
||||||
private static final String SENDER_PREAUTH = "+14157777777";
|
private static final String SENDER_PREAUTH = "+14157777777";
|
||||||
private static final String SENDER_REG_LOCK = "+14158888888";
|
private static final String SENDER_REG_LOCK = "+14158888888";
|
||||||
|
private static final String SENDER_HAS_STORAGE = "+14159999999";
|
||||||
|
|
||||||
private static final UUID SENDER_REG_LOCK_UUID = UUID.randomUUID();
|
private static final UUID SENDER_REG_LOCK_UUID = UUID.randomUUID();
|
||||||
|
|
||||||
|
@ -98,6 +100,7 @@ public class AccountControllerTest {
|
||||||
private TurnTokenGenerator turnTokenGenerator = mock(TurnTokenGenerator.class);
|
private TurnTokenGenerator turnTokenGenerator = mock(TurnTokenGenerator.class);
|
||||||
private Account senderPinAccount = mock(Account.class);
|
private Account senderPinAccount = mock(Account.class);
|
||||||
private Account senderRegLockAccount = mock(Account.class);
|
private Account senderRegLockAccount = mock(Account.class);
|
||||||
|
private Account senderHasStorage = mock(Account.class);
|
||||||
private RecaptchaClient recaptchaClient = mock(RecaptchaClient.class);
|
private RecaptchaClient recaptchaClient = mock(RecaptchaClient.class);
|
||||||
private GCMSender gcmSender = mock(GCMSender.class);
|
private GCMSender gcmSender = mock(GCMSender.class);
|
||||||
private APNSender apnSender = mock(APNSender.class);
|
private APNSender apnSender = mock(APNSender.class);
|
||||||
|
@ -146,12 +149,14 @@ public class AccountControllerTest {
|
||||||
|
|
||||||
when(timeProvider.getCurrentTimeMillis()).thenReturn(System.currentTimeMillis());
|
when(timeProvider.getCurrentTimeMillis()).thenReturn(System.currentTimeMillis());
|
||||||
|
|
||||||
when(senderPinAccount.getPin()).thenReturn(Optional.of("31337"));
|
|
||||||
when(senderPinAccount.getLastSeen()).thenReturn(System.currentTimeMillis());
|
when(senderPinAccount.getLastSeen()).thenReturn(System.currentTimeMillis());
|
||||||
|
when(senderPinAccount.getRegistrationLock()).thenReturn(new StoredRegistrationLock(Optional.empty(), Optional.empty(), Optional.of("31337"), System.currentTimeMillis()));
|
||||||
|
|
||||||
when(senderRegLockAccount.getPin()).thenReturn(Optional.empty());
|
when(senderHasStorage.getUuid()).thenReturn(UUID.randomUUID());
|
||||||
when(senderRegLockAccount.getRegistrationLock()).thenReturn(Optional.of(registrationLockCredentials.getHashedAuthenticationToken()));
|
when(senderHasStorage.isStorageSupported()).thenReturn(true);
|
||||||
when(senderRegLockAccount.getRegistrationLockSalt()).thenReturn(Optional.of(registrationLockCredentials.getSalt()));
|
when(senderHasStorage.getRegistrationLock()).thenReturn(new StoredRegistrationLock(Optional.empty(), Optional.empty(), Optional.empty(), System.currentTimeMillis()));
|
||||||
|
|
||||||
|
when(senderRegLockAccount.getRegistrationLock()).thenReturn(new StoredRegistrationLock(Optional.of(registrationLockCredentials.getHashedAuthenticationToken()), Optional.of(registrationLockCredentials.getSalt()), Optional.empty(), System.currentTimeMillis()));
|
||||||
when(senderRegLockAccount.getLastSeen()).thenReturn(System.currentTimeMillis());
|
when(senderRegLockAccount.getLastSeen()).thenReturn(System.currentTimeMillis());
|
||||||
when(senderRegLockAccount.getUuid()).thenReturn(SENDER_REG_LOCK_UUID);
|
when(senderRegLockAccount.getUuid()).thenReturn(SENDER_REG_LOCK_UUID);
|
||||||
|
|
||||||
|
@ -161,6 +166,7 @@ public class AccountControllerTest {
|
||||||
when(pendingAccountsManager.getCodeForNumber(SENDER_REG_LOCK)).thenReturn(Optional.of(new StoredVerificationCode("666666", System.currentTimeMillis(), null)));
|
when(pendingAccountsManager.getCodeForNumber(SENDER_REG_LOCK)).thenReturn(Optional.of(new StoredVerificationCode("666666", System.currentTimeMillis(), null)));
|
||||||
when(pendingAccountsManager.getCodeForNumber(SENDER_OVER_PIN)).thenReturn(Optional.of(new StoredVerificationCode("444444", System.currentTimeMillis(), null)));
|
when(pendingAccountsManager.getCodeForNumber(SENDER_OVER_PIN)).thenReturn(Optional.of(new StoredVerificationCode("444444", System.currentTimeMillis(), null)));
|
||||||
when(pendingAccountsManager.getCodeForNumber(SENDER_PREAUTH)).thenReturn(Optional.of(new StoredVerificationCode("555555", System.currentTimeMillis(), "validchallenge")));
|
when(pendingAccountsManager.getCodeForNumber(SENDER_PREAUTH)).thenReturn(Optional.of(new StoredVerificationCode("555555", System.currentTimeMillis(), "validchallenge")));
|
||||||
|
when(pendingAccountsManager.getCodeForNumber(SENDER_HAS_STORAGE)).thenReturn(Optional.of(new StoredVerificationCode("666666", System.currentTimeMillis(), null)));
|
||||||
|
|
||||||
when(accountsManager.get(eq(SENDER_PIN))).thenReturn(Optional.of(senderPinAccount));
|
when(accountsManager.get(eq(SENDER_PIN))).thenReturn(Optional.of(senderPinAccount));
|
||||||
when(accountsManager.get(eq(SENDER_REG_LOCK))).thenReturn(Optional.of(senderRegLockAccount));
|
when(accountsManager.get(eq(SENDER_REG_LOCK))).thenReturn(Optional.of(senderRegLockAccount));
|
||||||
|
@ -168,6 +174,7 @@ public class AccountControllerTest {
|
||||||
when(accountsManager.get(eq(SENDER))).thenReturn(Optional.empty());
|
when(accountsManager.get(eq(SENDER))).thenReturn(Optional.empty());
|
||||||
when(accountsManager.get(eq(SENDER_OLD))).thenReturn(Optional.empty());
|
when(accountsManager.get(eq(SENDER_OLD))).thenReturn(Optional.empty());
|
||||||
when(accountsManager.get(eq(SENDER_PREAUTH))).thenReturn(Optional.empty());
|
when(accountsManager.get(eq(SENDER_PREAUTH))).thenReturn(Optional.empty());
|
||||||
|
when(accountsManager.get(eq(SENDER_HAS_STORAGE))).thenReturn(Optional.of(senderHasStorage));
|
||||||
|
|
||||||
when(usernamesManager.put(eq(AuthHelper.VALID_UUID), eq("n00bkiller"))).thenReturn(true);
|
when(usernamesManager.put(eq(AuthHelper.VALID_UUID), eq("n00bkiller"))).thenReturn(true);
|
||||||
when(usernamesManager.put(eq(AuthHelper.VALID_UUID), eq("takenusername"))).thenReturn(false);
|
when(usernamesManager.put(eq(AuthHelper.VALID_UUID), eq("takenusername"))).thenReturn(false);
|
||||||
|
@ -485,11 +492,29 @@ public class AccountControllerTest {
|
||||||
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
||||||
|
|
||||||
assertThat(result.getUuid()).isNotNull();
|
assertThat(result.getUuid()).isNotNull();
|
||||||
|
assertThat(result.getBackupCredentials()).isNull();
|
||||||
|
|
||||||
verify(accountsManager, times(1)).create(isA(Account.class));
|
verify(accountsManager, times(1)).create(isA(Account.class));
|
||||||
verify(directoryQueue, times(1)).deleteRegisteredUser(notNull(), eq(SENDER));
|
verify(directoryQueue, times(1)).deleteRegisteredUser(notNull(), eq(SENDER));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVerifySupportsStorage() throws Exception {
|
||||||
|
AccountCreationResult result =
|
||||||
|
resources.getJerseyTest()
|
||||||
|
.target(String.format("/v1/accounts/code/%s", "666666"))
|
||||||
|
.request()
|
||||||
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER_HAS_STORAGE, "bar"))
|
||||||
|
.put(Entity.entity(new AccountAttributes("keykeykeykey", false, 2222, null),
|
||||||
|
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
||||||
|
|
||||||
|
assertThat(result.getUuid()).isNotNull();
|
||||||
|
assertThat(result.getBackupCredentials()).isNotNull();
|
||||||
|
|
||||||
|
verify(accountsManager, times(1)).create(isA(Account.class));
|
||||||
|
verify(directoryQueue, times(1)).deleteRegisteredUser(notNull(), eq(SENDER_HAS_STORAGE));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testVerifyCodeOld() throws Exception {
|
public void testVerifyCodeOld() throws Exception {
|
||||||
Response response =
|
Response response =
|
||||||
|
@ -550,6 +575,29 @@ public class AccountControllerTest {
|
||||||
verify(pinLimiter).validate(eq(SENDER_REG_LOCK));
|
verify(pinLimiter).validate(eq(SENDER_REG_LOCK));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVerifyRegistrationLockOld() throws Exception {
|
||||||
|
StoredRegistrationLock lock = senderRegLockAccount.getRegistrationLock();
|
||||||
|
|
||||||
|
try {
|
||||||
|
when(senderRegLockAccount.getRegistrationLock()).thenReturn(lock.forTime(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7)));
|
||||||
|
|
||||||
|
AccountCreationResult result =
|
||||||
|
resources.getJerseyTest()
|
||||||
|
.target(String.format("/v1/accounts/code/%s", "666666"))
|
||||||
|
.request()
|
||||||
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER_REG_LOCK, "bar"))
|
||||||
|
.put(Entity.entity(new AccountAttributes("keykeykeykey", false, 3333, null, null, null),
|
||||||
|
MediaType.APPLICATION_JSON_TYPE), AccountCreationResult.class);
|
||||||
|
|
||||||
|
assertThat(result.getUuid()).isNotNull();
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(pinLimiter);
|
||||||
|
} finally {
|
||||||
|
when(senderRegLockAccount.getRegistrationLock()).thenReturn(lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testVerifyWrongPin() throws Exception {
|
public void testVerifyWrongPin() throws Exception {
|
||||||
Response response =
|
Response response =
|
||||||
|
@ -639,7 +687,7 @@ public class AccountControllerTest {
|
||||||
@Test
|
@Test
|
||||||
public void testVerifyOldPin() throws Exception {
|
public void testVerifyOldPin() throws Exception {
|
||||||
try {
|
try {
|
||||||
when(senderPinAccount.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7));
|
when(senderPinAccount.getRegistrationLock()).thenReturn(new StoredRegistrationLock(Optional.empty(), Optional.empty(), Optional.of("31337"), System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7)));
|
||||||
|
|
||||||
AccountCreationResult result =
|
AccountCreationResult result =
|
||||||
resources.getJerseyTest()
|
resources.getJerseyTest()
|
||||||
|
@ -652,7 +700,7 @@ public class AccountControllerTest {
|
||||||
assertThat(result.getUuid()).isNotNull();
|
assertThat(result.getUuid()).isNotNull();
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
when(senderPinAccount.getLastSeen()).thenReturn(System.currentTimeMillis());
|
when(senderPinAccount.getRegistrationLock()).thenReturn(new StoredRegistrationLock(Optional.empty(), Optional.empty(), Optional.of("31337"), System.currentTimeMillis()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -669,8 +717,7 @@ public class AccountControllerTest {
|
||||||
assertThat(response.getStatus()).isEqualTo(204);
|
assertThat(response.getStatus()).isEqualTo(204);
|
||||||
|
|
||||||
verify(AuthHelper.VALID_ACCOUNT).setPin(eq("31337"));
|
verify(AuthHelper.VALID_ACCOUNT).setPin(eq("31337"));
|
||||||
verify(AuthHelper.VALID_ACCOUNT).setRegistrationLock(eq(null));
|
verify(AuthHelper.VALID_ACCOUNT).setRegistrationLock(eq(null), eq(null));
|
||||||
verify(AuthHelper.VALID_ACCOUNT).setRegistrationLockSalt(eq(null));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -688,8 +735,7 @@ public class AccountControllerTest {
|
||||||
ArgumentCaptor<String> pinSaltCapture = ArgumentCaptor.forClass(String.class);
|
ArgumentCaptor<String> pinSaltCapture = ArgumentCaptor.forClass(String.class);
|
||||||
|
|
||||||
verify(AuthHelper.VALID_ACCOUNT, times(1)).setPin(eq(null));
|
verify(AuthHelper.VALID_ACCOUNT, times(1)).setPin(eq(null));
|
||||||
verify(AuthHelper.VALID_ACCOUNT, times(1)).setRegistrationLock(pinCapture.capture());
|
verify(AuthHelper.VALID_ACCOUNT, times(1)).setRegistrationLock(pinCapture.capture(), pinSaltCapture.capture());
|
||||||
verify(AuthHelper.VALID_ACCOUNT, times(1)).setRegistrationLockSalt(pinSaltCapture.capture());
|
|
||||||
|
|
||||||
assertThat(pinCapture.getValue()).isNotEmpty();
|
assertThat(pinCapture.getValue()).isNotEmpty();
|
||||||
assertThat(pinSaltCapture.getValue()).isNotEmpty();
|
assertThat(pinSaltCapture.getValue()).isNotEmpty();
|
||||||
|
|
|
@ -85,13 +85,13 @@ public class MessageControllerTest {
|
||||||
@Before
|
@Before
|
||||||
public void setup() throws Exception {
|
public void setup() throws Exception {
|
||||||
Set<Device> singleDeviceList = new HashSet<Device>() {{
|
Set<Device> singleDeviceList = new HashSet<Device>() {{
|
||||||
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, new SignedPreKey(333, "baz", "boop"), System.currentTimeMillis(), System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, true)));
|
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 111, new SignedPreKey(333, "baz", "boop"), System.currentTimeMillis(), System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, true, true)));
|
||||||
}};
|
}};
|
||||||
|
|
||||||
Set<Device> multiDeviceList = new HashSet<Device>() {{
|
Set<Device> multiDeviceList = new HashSet<Device>() {{
|
||||||
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, true)));
|
add(new Device(1, null, "foo", "bar", "baz", "isgcm", null, null, false, 222, new SignedPreKey(111, "foo", "bar"), System.currentTimeMillis(), System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, true, true)));
|
||||||
add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(), System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, true)));
|
add(new Device(2, null, "foo", "bar", "baz", "isgcm", null, null, false, 333, new SignedPreKey(222, "oof", "rab"), System.currentTimeMillis(), System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(true, true, true)));
|
||||||
add(new Device(3, null, "foo", "bar", "baz", "isgcm", null, null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31), System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(false, false)));
|
add(new Device(3, null, "foo", "bar", "baz", "isgcm", null, null, false, 444, null, System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31), System.currentTimeMillis(), "Test", 0, new Device.DeviceCapabilities(false, false, false)));
|
||||||
}};
|
}};
|
||||||
|
|
||||||
Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, SINGLE_DEVICE_UUID, singleDeviceList, "1234".getBytes());
|
Account singleDeviceAccount = new Account(SINGLE_DEVICE_RECIPIENT, SINGLE_DEVICE_UUID, singleDeviceList, "1234".getBytes());
|
||||||
|
|
|
@ -49,15 +49,15 @@ public class AccountTest {
|
||||||
when(oldSecondaryDevice.isEnabled()).thenReturn(false);
|
when(oldSecondaryDevice.isEnabled()).thenReturn(false);
|
||||||
when(oldSecondaryDevice.getId()).thenReturn(2L);
|
when(oldSecondaryDevice.getId()).thenReturn(2L);
|
||||||
|
|
||||||
when(uuidCapableDevice.getCapabilities()).thenReturn(new Device.DeviceCapabilities(true, true));
|
when(uuidCapableDevice.getCapabilities()).thenReturn(new Device.DeviceCapabilities(true, true, true));
|
||||||
when(uuidCapableDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1));
|
when(uuidCapableDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1));
|
||||||
when(uuidCapableDevice.isEnabled()).thenReturn(true);
|
when(uuidCapableDevice.isEnabled()).thenReturn(true);
|
||||||
|
|
||||||
when(uuidIncapableDevice.getCapabilities()).thenReturn(new Device.DeviceCapabilities(false, false));
|
when(uuidIncapableDevice.getCapabilities()).thenReturn(new Device.DeviceCapabilities(false, false, false));
|
||||||
when(uuidIncapableDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1));
|
when(uuidIncapableDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(1));
|
||||||
when(uuidIncapableDevice.isEnabled()).thenReturn(true);
|
when(uuidIncapableDevice.isEnabled()).thenReturn(true);
|
||||||
|
|
||||||
when(uuidIncapableExpiredDevice.getCapabilities()).thenReturn(new Device.DeviceCapabilities(false, false));
|
when(uuidIncapableExpiredDevice.getCapabilities()).thenReturn(new Device.DeviceCapabilities(false, false, false));
|
||||||
when(uuidIncapableExpiredDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31));
|
when(uuidIncapableExpiredDevice.getLastSeen()).thenReturn(System.currentTimeMillis() - TimeUnit.DAYS.toMillis(31));
|
||||||
when(uuidIncapableExpiredDevice.isEnabled()).thenReturn(false);
|
when(uuidIncapableExpiredDevice.isEnabled()).thenReturn(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -270,7 +270,7 @@ public class AccountsTest {
|
||||||
private Device generateDevice(long id) {
|
private Device generateDevice(long id) {
|
||||||
Random random = new Random(System.currentTimeMillis());
|
Random random = new Random(System.currentTimeMillis());
|
||||||
SignedPreKey signedPreKey = new SignedPreKey(random.nextInt(), "testPublicKey-" + random.nextInt(), "testSignature-" + random.nextInt());
|
SignedPreKey signedPreKey = new SignedPreKey(random.nextInt(), "testPublicKey-" + random.nextInt(), "testSignature-" + random.nextInt());
|
||||||
return new Device(id, "testName-" + random.nextInt(), "testAuthToken-" + random.nextInt(), "testSalt-" + random.nextInt(), null, "testGcmId-" + random.nextInt(), "testApnId-" + random.nextInt(), "testVoipApnId-" + random.nextInt(), random.nextBoolean(), random.nextInt(), signedPreKey, random.nextInt(), random.nextInt(), "testUserAgent-" + random.nextInt() , 0, new Device.DeviceCapabilities(random.nextBoolean(), random.nextBoolean()));
|
return new Device(id, "testName-" + random.nextInt(), "testAuthToken-" + random.nextInt(), "testSalt-" + random.nextInt(), null, "testGcmId-" + random.nextInt(), "testApnId-" + random.nextInt(), "testVoipApnId-" + random.nextInt(), random.nextBoolean(), random.nextInt(), signedPreKey, random.nextInt(), random.nextInt(), "testUserAgent-" + random.nextInt() , 0, new Device.DeviceCapabilities(random.nextBoolean(), random.nextBoolean(), random.nextBoolean()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Account generateAccount(String number, UUID uuid) {
|
private Account generateAccount(String number, UUID uuid) {
|
||||||
|
|
Loading…
Reference in New Issue