mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-22 00:38:02 +01:00
Support for UUID based addressing
This commit is contained in:
@@ -33,6 +33,7 @@ import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
|
||||
import org.whispersystems.textsecuregcm.auth.TurnToken;
|
||||
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
||||
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
||||
import org.whispersystems.textsecuregcm.entities.AccountCreationResult;
|
||||
import org.whispersystems.textsecuregcm.entities.ApnRegistrationId;
|
||||
import org.whispersystems.textsecuregcm.entities.DeviceName;
|
||||
import org.whispersystems.textsecuregcm.entities.GcmRegistrationId;
|
||||
@@ -78,6 +79,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static com.codahale.metrics.MetricRegistry.name;
|
||||
@@ -245,17 +247,21 @@ public class AccountController {
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Path("/code/{verification_code}")
|
||||
public void verifyAccount(@PathParam("verification_code") String verificationCode,
|
||||
@HeaderParam("Authorization") String authorizationHeader,
|
||||
@HeaderParam("X-Signal-Agent") String userAgent,
|
||||
@Valid AccountAttributes accountAttributes)
|
||||
public AccountCreationResult verifyAccount(@PathParam("verification_code") String verificationCode,
|
||||
@HeaderParam("Authorization") String authorizationHeader,
|
||||
@HeaderParam("X-Signal-Agent") String userAgent,
|
||||
@Valid AccountAttributes accountAttributes)
|
||||
throws RateLimitExceededException
|
||||
{
|
||||
try {
|
||||
AuthorizationHeader header = AuthorizationHeader.fromFullHeader(authorizationHeader);
|
||||
String number = header.getNumber();
|
||||
String number = header.getIdentifier().getNumber();
|
||||
String password = header.getPassword();
|
||||
|
||||
if (number == null) {
|
||||
throw new WebApplicationException(400);
|
||||
}
|
||||
|
||||
rateLimiters.getVerifyLimiter().validate(number);
|
||||
|
||||
Optional<StoredVerificationCode> storedVerificationCode = pendingAccounts.getCodeForNumber(number);
|
||||
@@ -308,9 +314,11 @@ public class AccountController {
|
||||
rateLimiters.getPinLimiter().clear(number);
|
||||
}
|
||||
|
||||
createAccount(number, password, userAgent, accountAttributes);
|
||||
Account account = createAccount(number, password, userAgent, accountAttributes);
|
||||
|
||||
metricRegistry.meter(name(AccountController.class, "verify", Util.getCountryCode(number))).mark();
|
||||
|
||||
return new AccountCreationResult(account.getUuid());
|
||||
} catch (InvalidAuthorizationHeaderException e) {
|
||||
logger.info("Bad Authorization Header", e);
|
||||
throw new WebApplicationException(Response.status(401).build());
|
||||
@@ -502,6 +510,13 @@ public class AccountController {
|
||||
accounts.update(account);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/whoami")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public AccountCreationResult whoAmI(@Auth Account account) {
|
||||
return new AccountCreationResult(account.getUuid());
|
||||
}
|
||||
|
||||
private CaptchaRequirement requiresCaptcha(String number, String transport, String forwardedFor,
|
||||
String requester,
|
||||
Optional<String> captchaToken,
|
||||
@@ -576,7 +591,7 @@ public class AccountController {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void createAccount(String number, String password, String userAgent, AccountAttributes accountAttributes) {
|
||||
private Account createAccount(String number, String password, String userAgent, AccountAttributes accountAttributes) {
|
||||
Device device = new Device();
|
||||
device.setId(Device.MASTER_ID);
|
||||
device.setAuthenticationCredentials(new AuthenticationCredentials(password));
|
||||
@@ -591,6 +606,7 @@ public class AccountController {
|
||||
|
||||
Account account = new Account();
|
||||
account.setNumber(number);
|
||||
account.setUuid(UUID.randomUUID());
|
||||
account.addDevice(device);
|
||||
account.setPin(accountAttributes.getPin());
|
||||
account.setUnidentifiedAccessKey(accountAttributes.getUnidentifiedAccessKey());
|
||||
@@ -608,6 +624,8 @@ public class AccountController {
|
||||
|
||||
messagesManager.clear(number);
|
||||
pendingAccounts.remove(number);
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
@VisibleForTesting protected
|
||||
|
||||
@@ -164,9 +164,11 @@ public class DeviceController {
|
||||
{
|
||||
try {
|
||||
AuthorizationHeader header = AuthorizationHeader.fromFullHeader(authorizationHeader);
|
||||
String number = header.getNumber();
|
||||
String number = header.getIdentifier().getNumber();
|
||||
String password = header.getPassword();
|
||||
|
||||
if (number == null) throw new WebApplicationException(400);
|
||||
|
||||
rateLimiters.getVerifyDeviceLimiter().validate(number);
|
||||
|
||||
Optional<StoredVerificationCode> storedVerificationCode = pendingDevices.getCodeForNumber(number);
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.whispersystems.textsecuregcm.controllers;
|
||||
import com.codahale.metrics.annotation.Timed;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
|
||||
import org.whispersystems.textsecuregcm.auth.Anonymous;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
|
||||
@@ -115,11 +116,11 @@ public class KeysController {
|
||||
|
||||
@Timed
|
||||
@GET
|
||||
@Path("/{number}/{device_id}")
|
||||
@Path("/{identifier}/{device_id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Optional<PreKeyResponse> getDeviceKeys(@Auth Optional<Account> account,
|
||||
@HeaderParam(OptionalAccess.UNIDENTIFIED) Optional<Anonymous> accessKey,
|
||||
@PathParam("number") String number,
|
||||
@PathParam("identifier") AmbiguousIdentifier targetName,
|
||||
@PathParam("device_id") String deviceId)
|
||||
throws RateLimitExceededException
|
||||
{
|
||||
@@ -127,13 +128,13 @@ public class KeysController {
|
||||
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
Optional<Account> target = accounts.get(number);
|
||||
Optional<Account> target = accounts.get(targetName);
|
||||
OptionalAccess.verify(account, accessKey, target, deviceId);
|
||||
|
||||
assert(target.isPresent());
|
||||
|
||||
if (account.isPresent()) {
|
||||
rateLimiters.getPreKeysLimiter().validate(account.get().getNumber() + "__" + number + "." + deviceId);
|
||||
rateLimiters.getPreKeysLimiter().validate(account.get().getNumber() + "__" + target.get().getNumber() + "." + deviceId);
|
||||
}
|
||||
|
||||
List<KeyRecord> targetKeys = getLocalKeys(target.get(), deviceId);
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.codahale.metrics.annotation.Timed;
|
||||
import com.google.protobuf.ByteString;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
|
||||
import org.whispersystems.textsecuregcm.auth.Anonymous;
|
||||
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
|
||||
import org.whispersystems.textsecuregcm.entities.IncomingMessage;
|
||||
@@ -109,7 +110,7 @@ public class MessageController {
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public SendMessageResponse sendMessage(@Auth Optional<Account> source,
|
||||
@HeaderParam(OptionalAccess.UNIDENTIFIED) Optional<Anonymous> accessKey,
|
||||
@PathParam("destination") String destinationName,
|
||||
@PathParam("destination") AmbiguousIdentifier destinationName,
|
||||
@Valid IncomingMessageList messages)
|
||||
throws RateLimitExceededException
|
||||
{
|
||||
@@ -117,18 +118,18 @@ public class MessageController {
|
||||
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
if (source.isPresent() && !source.get().getNumber().equals(destinationName)) {
|
||||
if (source.isPresent() && !source.get().isFor(destinationName)) {
|
||||
rateLimiters.getMessagesLimiter().validate(source.get().getNumber() + "__" + destinationName);
|
||||
}
|
||||
|
||||
if (source.isPresent() && !source.get().getNumber().equals(destinationName)) {
|
||||
if (source.isPresent() && !source.get().isFor(destinationName)) {
|
||||
identifiedMeter.mark();
|
||||
} else {
|
||||
} else if (!source.isPresent()) {
|
||||
unidentifiedMeter.mark();
|
||||
}
|
||||
|
||||
try {
|
||||
boolean isSyncMessage = source.isPresent() && source.get().getNumber().equals(destinationName);
|
||||
boolean isSyncMessage = source.isPresent() && source.get().isFor(destinationName);
|
||||
|
||||
Optional<Account> destination;
|
||||
|
||||
@@ -246,6 +247,7 @@ public class MessageController {
|
||||
|
||||
if (source.isPresent()) {
|
||||
messageBuilder.setSource(source.get().getNumber())
|
||||
.setSourceUuid(source.get().getUuid().toString())
|
||||
.setSourceDevice((int)source.get().getAuthenticatedDevice().get().getId());
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.codahale.metrics.annotation.Timed;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
import org.hibernate.validator.valuehandling.UnwrapValidatedValue;
|
||||
import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
|
||||
import org.whispersystems.textsecuregcm.auth.Anonymous;
|
||||
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
|
||||
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessChecksum;
|
||||
@@ -79,10 +80,10 @@ public class ProfileController {
|
||||
@Timed
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Path("/{number}")
|
||||
@Path("/{identifier}")
|
||||
public Profile getProfile(@Auth Optional<Account> requestAccount,
|
||||
@HeaderParam(OptionalAccess.UNIDENTIFIED) Optional<Anonymous> accessKey,
|
||||
@PathParam("number") String number,
|
||||
@PathParam("identifier") AmbiguousIdentifier identifier,
|
||||
@QueryParam("ca") boolean useCaCertificate)
|
||||
throws RateLimitExceededException
|
||||
{
|
||||
@@ -94,7 +95,7 @@ public class ProfileController {
|
||||
rateLimiters.getProfileLimiter().validate(requestAccount.get().getNumber());
|
||||
}
|
||||
|
||||
Optional<Account> accountProfile = accountsManager.get(number);
|
||||
Optional<Account> accountProfile = accountsManager.get(identifier);
|
||||
OptionalAccess.verify(requestAccount, accessKey, accountProfile);
|
||||
|
||||
//noinspection ConstantConditions,OptionalGetWithoutIsPresent
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package org.whispersystems.textsecuregcm.controllers;
|
||||
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.PublicAccount;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@Path("/v1/transparency/")
|
||||
public class TransparentDataController {
|
||||
|
||||
private final AccountsManager accountsManager;
|
||||
private final Map<String, String> transparentDataIndex;
|
||||
|
||||
public TransparentDataController(AccountsManager accountsManager,
|
||||
Map<String, String> transparentDataIndex)
|
||||
{
|
||||
this.accountsManager = accountsManager;
|
||||
this.transparentDataIndex = transparentDataIndex;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/account/{id}")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Optional<PublicAccount> getAccount(@PathParam("id") String id) {
|
||||
String index = transparentDataIndex.get(id);
|
||||
|
||||
if (index != null) {
|
||||
return accountsManager.get(index).map(PublicAccount::new);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user