mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-22 05:58:05 +01:00
Always require atomic account creation
This commit is contained in:
committed by
Jon Chambers
parent
9069c5abb6
commit
521900c048
@@ -77,12 +77,12 @@ public class DeviceController {
|
||||
static final int MAX_DEVICES = 6;
|
||||
|
||||
private final Key verificationTokenKey;
|
||||
private final AccountsManager accounts;
|
||||
private final MessagesManager messages;
|
||||
private final AccountsManager accounts;
|
||||
private final MessagesManager messages;
|
||||
private final KeysManager keys;
|
||||
private final RateLimiters rateLimiters;
|
||||
private final RateLimiters rateLimiters;
|
||||
private final FaultTolerantRedisCluster usedTokenCluster;
|
||||
private final Map<String, Integer> maxDeviceConfiguration;
|
||||
private final Map<String, Integer> maxDeviceConfiguration;
|
||||
|
||||
private final Clock clock;
|
||||
|
||||
@@ -217,8 +217,8 @@ public class DeviceController {
|
||||
@ChangesDeviceEnabledState
|
||||
@Operation(summary = "Link a device to an account",
|
||||
description = """
|
||||
Links a device to an account identified by a given phone number.
|
||||
""")
|
||||
Links a device to an account identified by a given phone number.
|
||||
""")
|
||||
@ApiResponse(responseCode = "200", description = "The new device was linked to the calling account", useReturnTypeSchema = true)
|
||||
@ApiResponse(responseCode = "403", description = "The given account was not found or the given verification code was incorrect")
|
||||
@ApiResponse(responseCode = "411", description = "The given account already has its maximum number of linked devices")
|
||||
@@ -227,8 +227,8 @@ public class DeviceController {
|
||||
name = "Retry-After",
|
||||
description = "If present, an positive integer indicating the number of seconds before a subsequent attempt could succeed"))
|
||||
public DeviceResponse linkDevice(@HeaderParam(HttpHeaders.AUTHORIZATION) BasicAuthorizationHeader authorizationHeader,
|
||||
@NotNull @Valid LinkDeviceRequest linkDeviceRequest,
|
||||
@Context ContainerRequest containerRequest)
|
||||
@NotNull @Valid LinkDeviceRequest linkDeviceRequest,
|
||||
@Context ContainerRequest containerRequest)
|
||||
throws RateLimitExceededException, DeviceLimitExceededException {
|
||||
|
||||
final Pair<Account, Device> accountAndDevice = createDevice(authorizationHeader.getPassword(),
|
||||
@@ -296,7 +296,8 @@ public class DeviceController {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
final byte[] expectedSignature = getInitializedMac().doFinal(claimsAndSignature[0].getBytes(StandardCharsets.UTF_8));
|
||||
final byte[] expectedSignature = getInitializedMac().doFinal(
|
||||
claimsAndSignature[0].getBytes(StandardCharsets.UTF_8));
|
||||
final byte[] providedSignature;
|
||||
|
||||
try {
|
||||
@@ -345,10 +346,10 @@ public class DeviceController {
|
||||
}
|
||||
|
||||
private Pair<Account, Device> createDevice(final String password,
|
||||
final String verificationCode,
|
||||
final AccountAttributes accountAttributes,
|
||||
final ContainerRequest containerRequest,
|
||||
final Optional<DeviceActivationRequest> maybeDeviceActivationRequest)
|
||||
final String verificationCode,
|
||||
final AccountAttributes accountAttributes,
|
||||
final ContainerRequest containerRequest,
|
||||
final Optional<DeviceActivationRequest> maybeDeviceActivationRequest)
|
||||
throws RateLimitExceededException, DeviceLimitExceededException {
|
||||
|
||||
final Optional<UUID> maybeAciFromToken = checkVerificationToken(verificationCode);
|
||||
@@ -359,16 +360,11 @@ public class DeviceController {
|
||||
rateLimiters.getVerifyDeviceLimiter().validate(account.getUuid());
|
||||
|
||||
maybeDeviceActivationRequest.ifPresent(deviceActivationRequest -> {
|
||||
assert deviceActivationRequest.aciSignedPreKey().isPresent();
|
||||
assert deviceActivationRequest.pniSignedPreKey().isPresent();
|
||||
assert deviceActivationRequest.aciPqLastResortPreKey().isPresent();
|
||||
assert deviceActivationRequest.pniPqLastResortPreKey().isPresent();
|
||||
|
||||
final boolean allKeysValid = PreKeySignatureValidator.validatePreKeySignatures(account.getIdentityKey(
|
||||
IdentityType.ACI),
|
||||
List.of(deviceActivationRequest.aciSignedPreKey().get(), deviceActivationRequest.aciPqLastResortPreKey().get()))
|
||||
&& PreKeySignatureValidator.validatePreKeySignatures(account.getIdentityKey(IdentityType.PNI),
|
||||
List.of(deviceActivationRequest.pniSignedPreKey().get(), deviceActivationRequest.pniPqLastResortPreKey().get()));
|
||||
final boolean allKeysValid =
|
||||
PreKeySignatureValidator.validatePreKeySignatures(account.getIdentityKey(IdentityType.ACI),
|
||||
List.of(deviceActivationRequest.aciSignedPreKey(), deviceActivationRequest.aciPqLastResortPreKey()))
|
||||
&& PreKeySignatureValidator.validatePreKeySignatures(account.getIdentityKey(IdentityType.PNI),
|
||||
List.of(deviceActivationRequest.pniSignedPreKey(), deviceActivationRequest.pniPqLastResortPreKey()));
|
||||
|
||||
if (!allKeysValid) {
|
||||
throw new WebApplicationException(Response.status(422).build());
|
||||
@@ -406,8 +402,8 @@ public class DeviceController {
|
||||
device.setCapabilities(accountAttributes.getCapabilities());
|
||||
|
||||
maybeDeviceActivationRequest.ifPresent(deviceActivationRequest -> {
|
||||
device.setSignedPreKey(deviceActivationRequest.aciSignedPreKey().get());
|
||||
device.setPhoneNumberIdentitySignedPreKey(deviceActivationRequest.pniSignedPreKey().get());
|
||||
device.setSignedPreKey(deviceActivationRequest.aciSignedPreKey());
|
||||
device.setPhoneNumberIdentitySignedPreKey(deviceActivationRequest.pniSignedPreKey());
|
||||
|
||||
deviceActivationRequest.apnToken().ifPresent(apnRegistrationId -> {
|
||||
device.setApnId(apnRegistrationId.apnRegistrationId());
|
||||
@@ -431,13 +427,13 @@ public class DeviceController {
|
||||
|
||||
maybeDeviceActivationRequest.ifPresent(deviceActivationRequest -> CompletableFuture.allOf(
|
||||
keys.storeEcSignedPreKeys(a.getUuid(),
|
||||
Map.of(device.getId(), deviceActivationRequest.aciSignedPreKey().get())),
|
||||
Map.of(device.getId(), deviceActivationRequest.aciSignedPreKey())),
|
||||
keys.storePqLastResort(a.getUuid(),
|
||||
Map.of(device.getId(), deviceActivationRequest.aciPqLastResortPreKey().get())),
|
||||
Map.of(device.getId(), deviceActivationRequest.aciPqLastResortPreKey())),
|
||||
keys.storeEcSignedPreKeys(a.getPhoneNumberIdentifier(),
|
||||
Map.of(device.getId(), deviceActivationRequest.pniSignedPreKey().get())),
|
||||
Map.of(device.getId(), deviceActivationRequest.pniSignedPreKey())),
|
||||
keys.storePqLastResort(a.getPhoneNumberIdentifier(),
|
||||
Map.of(device.getId(), deviceActivationRequest.pniPqLastResortPreKey().get())))
|
||||
Map.of(device.getId(), deviceActivationRequest.pniPqLastResortPreKey())))
|
||||
.join());
|
||||
|
||||
a.addDevice(device);
|
||||
|
||||
@@ -64,7 +64,6 @@ public class RegistrationController {
|
||||
private static final String COUNTRY_CODE_TAG_NAME = "countryCode";
|
||||
private static final String REGION_CODE_TAG_NAME = "regionCode";
|
||||
private static final String VERIFICATION_TYPE_TAG_NAME = "verification";
|
||||
private static final String ACCOUNT_ACTIVATED_TAG_NAME = "accountActivated";
|
||||
private static final String INVALID_ACCOUNT_ATTRS_COUNTER_NAME = name(RegistrationController.class, "invalidAccountAttrs");
|
||||
|
||||
private final AccountsManager accounts;
|
||||
@@ -145,50 +144,39 @@ public class RegistrationController {
|
||||
Account account = accounts.create(number, password, signalAgent, registrationRequest.accountAttributes(),
|
||||
existingAccount.map(Account::getBadges).orElseGet(ArrayList::new));
|
||||
|
||||
// If the request includes all the information we need to fully "activate" the account, we should do so
|
||||
if (registrationRequest.supportsAtomicAccountCreation()) {
|
||||
assert registrationRequest.aciIdentityKey().isPresent();
|
||||
assert registrationRequest.pniIdentityKey().isPresent();
|
||||
assert registrationRequest.deviceActivationRequest().aciSignedPreKey().isPresent();
|
||||
assert registrationRequest.deviceActivationRequest().pniSignedPreKey().isPresent();
|
||||
assert registrationRequest.deviceActivationRequest().aciPqLastResortPreKey().isPresent();
|
||||
assert registrationRequest.deviceActivationRequest().pniPqLastResortPreKey().isPresent();
|
||||
account = accounts.update(account, a -> {
|
||||
a.setIdentityKey(registrationRequest.aciIdentityKey());
|
||||
a.setPhoneNumberIdentityKey(registrationRequest.pniIdentityKey());
|
||||
|
||||
account = accounts.update(account, a -> {
|
||||
a.setIdentityKey(registrationRequest.aciIdentityKey().get());
|
||||
a.setPhoneNumberIdentityKey(registrationRequest.pniIdentityKey().get());
|
||||
final Device device = a.getPrimaryDevice().orElseThrow();
|
||||
|
||||
final Device device = a.getPrimaryDevice().orElseThrow();
|
||||
device.setSignedPreKey(registrationRequest.deviceActivationRequest().aciSignedPreKey());
|
||||
device.setPhoneNumberIdentitySignedPreKey(registrationRequest.deviceActivationRequest().pniSignedPreKey());
|
||||
|
||||
device.setSignedPreKey(registrationRequest.deviceActivationRequest().aciSignedPreKey().get());
|
||||
device.setPhoneNumberIdentitySignedPreKey(registrationRequest.deviceActivationRequest().pniSignedPreKey().get());
|
||||
|
||||
registrationRequest.deviceActivationRequest().apnToken().ifPresent(apnRegistrationId -> {
|
||||
device.setApnId(apnRegistrationId.apnRegistrationId());
|
||||
device.setVoipApnId(apnRegistrationId.voipRegistrationId());
|
||||
});
|
||||
|
||||
registrationRequest.deviceActivationRequest().gcmToken().ifPresent(gcmRegistrationId ->
|
||||
device.setGcmId(gcmRegistrationId.gcmRegistrationId()));
|
||||
|
||||
CompletableFuture.allOf(
|
||||
keysManager.storeEcSignedPreKeys(a.getUuid(),
|
||||
Map.of(Device.PRIMARY_ID, registrationRequest.deviceActivationRequest().aciSignedPreKey().get())),
|
||||
keysManager.storePqLastResort(a.getUuid(),
|
||||
Map.of(Device.PRIMARY_ID, registrationRequest.deviceActivationRequest().aciPqLastResortPreKey().get())),
|
||||
keysManager.storeEcSignedPreKeys(a.getPhoneNumberIdentifier(),
|
||||
Map.of(Device.PRIMARY_ID, registrationRequest.deviceActivationRequest().pniSignedPreKey().get())),
|
||||
keysManager.storePqLastResort(a.getPhoneNumberIdentifier(),
|
||||
Map.of(Device.PRIMARY_ID, registrationRequest.deviceActivationRequest().pniPqLastResortPreKey().get())))
|
||||
.join();
|
||||
registrationRequest.deviceActivationRequest().apnToken().ifPresent(apnRegistrationId -> {
|
||||
device.setApnId(apnRegistrationId.apnRegistrationId());
|
||||
device.setVoipApnId(apnRegistrationId.voipRegistrationId());
|
||||
});
|
||||
}
|
||||
|
||||
registrationRequest.deviceActivationRequest().gcmToken().ifPresent(gcmRegistrationId ->
|
||||
device.setGcmId(gcmRegistrationId.gcmRegistrationId()));
|
||||
|
||||
CompletableFuture.allOf(
|
||||
keysManager.storeEcSignedPreKeys(a.getUuid(),
|
||||
Map.of(Device.PRIMARY_ID, registrationRequest.deviceActivationRequest().aciSignedPreKey())),
|
||||
keysManager.storePqLastResort(a.getUuid(),
|
||||
Map.of(Device.PRIMARY_ID, registrationRequest.deviceActivationRequest().aciPqLastResortPreKey())),
|
||||
keysManager.storeEcSignedPreKeys(a.getPhoneNumberIdentifier(),
|
||||
Map.of(Device.PRIMARY_ID, registrationRequest.deviceActivationRequest().pniSignedPreKey())),
|
||||
keysManager.storePqLastResort(a.getPhoneNumberIdentifier(),
|
||||
Map.of(Device.PRIMARY_ID, registrationRequest.deviceActivationRequest().pniPqLastResortPreKey())))
|
||||
.join();
|
||||
});
|
||||
|
||||
Metrics.counter(ACCOUNT_CREATED_COUNTER_NAME, Tags.of(UserAgentTagUtil.getPlatformTag(userAgent),
|
||||
Tag.of(COUNTRY_CODE_TAG_NAME, Util.getCountryCode(number)),
|
||||
Tag.of(REGION_CODE_TAG_NAME, Util.getRegion(number)),
|
||||
Tag.of(VERIFICATION_TYPE_TAG_NAME, verificationType.name()),
|
||||
Tag.of(ACCOUNT_ACTIVATED_TAG_NAME, String.valueOf(account.isEnabled()))))
|
||||
Tag.of(VERIFICATION_TYPE_TAG_NAME, verificationType.name())))
|
||||
.increment();
|
||||
|
||||
return new AccountIdentityResponse(account.getUuid(),
|
||||
|
||||
Reference in New Issue
Block a user