New API to support multiple accounts per # (FREEBIE)

This commit is contained in:
Matt Corallo
2014-01-07 13:35:58 -10:00
parent 4cd1082a4a
commit ef1160eda8
35 changed files with 1591 additions and 388 deletions

View File

@@ -16,6 +16,7 @@
*/
package org.whispersystems.textsecuregcm.controllers;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.yammer.dropwizard.auth.Auth;
import com.yammer.metrics.annotation.Timed;
@@ -33,6 +34,7 @@ import org.whispersystems.textsecuregcm.sms.TwilioSmsSender;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.PendingAccountsManager;
import org.whispersystems.textsecuregcm.storage.PendingDevicesManager;
import org.whispersystems.textsecuregcm.util.Util;
import org.whispersystems.textsecuregcm.util.VerificationCode;
@@ -58,17 +60,20 @@ public class AccountController {
private final Logger logger = LoggerFactory.getLogger(AccountController.class);
private final PendingAccountsManager pendingAccounts;
private final AccountsManager accounts;
private final RateLimiters rateLimiters;
private final SmsSender smsSender;
private final PendingAccountsManager pendingAccounts;
private final PendingDevicesManager pendingDevices;
private final AccountsManager accounts;
private final RateLimiters rateLimiters;
private final SmsSender smsSender;
public AccountController(PendingAccountsManager pendingAccounts,
AccountsManager accounts,
RateLimiters rateLimiters,
SmsSender smsSenderFactory)
PendingDevicesManager pendingDevices,
AccountsManager accounts,
RateLimiters rateLimiters,
SmsSender smsSenderFactory)
{
this.pendingAccounts = pendingAccounts;
this.pendingDevices = pendingDevices;
this.accounts = accounts;
this.rateLimiters = rateLimiters;
this.smsSender = smsSenderFactory;
@@ -119,8 +124,8 @@ public class AccountController {
throws RateLimitExceededException
{
try {
AuthorizationHeader header = new AuthorizationHeader(authorizationHeader);
String number = header.getUserName();
AuthorizationHeader header = AuthorizationHeader.fromFullHeader(authorizationHeader);
String number = header.getNumber();
String password = header.getPassword();
rateLimiters.getVerifyLimiter().validate(number);
@@ -138,16 +143,22 @@ public class AccountController {
account.setAuthenticationCredentials(new AuthenticationCredentials(password));
account.setSignalingKey(accountAttributes.getSignalingKey());
account.setSupportsSms(accountAttributes.getSupportsSms());
account.setFetchesMessages(accountAttributes.getFetchesMessages());
account.setDeviceId(0);
accounts.createResetNumber(account);
pendingAccounts.remove(number);
accounts.create(account);
logger.debug("Stored account...");
} catch (InvalidAuthorizationHeaderException e) {
logger.info("Bad Authorization Header", e);
throw new WebApplicationException(Response.status(401).build());
}
}
@Timed
@PUT
@Path("/gcm/")
@@ -190,10 +201,10 @@ public class AccountController {
@Produces(MediaType.APPLICATION_XML)
public Response getTwiml(@PathParam("code") String encodedVerificationText) {
return Response.ok().entity(String.format(TwilioSmsSender.SAY_TWIML,
encodedVerificationText)).build();
encodedVerificationText)).build();
}
private VerificationCode generateVerificationCode() {
@VisibleForTesting protected VerificationCode generateVerificationCode() {
try {
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
int randomInt = 100000 + random.nextInt(900000);
@@ -203,4 +214,64 @@ public class AccountController {
}
}
@Timed
@GET
@Path("/registerdevice")
@Produces(MediaType.APPLICATION_JSON)
public VerificationCode createDeviceToken(@Auth Account account)
throws RateLimitExceededException
{
rateLimiters.getVerifyLimiter().validate(account.getNumber()); //TODO: New limiter?
VerificationCode verificationCode = generateVerificationCode();
pendingDevices.store(account.getNumber(), verificationCode.getVerificationCode());
return verificationCode;
}
@Timed
@PUT
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/device/{verification_code}")
public long verifyDeviceToken(@PathParam("verification_code") String verificationCode,
@HeaderParam("Authorization") String authorizationHeader,
@Valid AccountAttributes accountAttributes)
throws RateLimitExceededException
{
Account account;
try {
AuthorizationHeader header = AuthorizationHeader.fromFullHeader(authorizationHeader);
String number = header.getNumber();
String password = header.getPassword();
rateLimiters.getVerifyLimiter().validate(number); //TODO: New limiter?
Optional<String> storedVerificationCode = pendingDevices.getCodeForNumber(number);
if (!storedVerificationCode.isPresent() ||
!verificationCode.equals(storedVerificationCode.get()))
{
throw new WebApplicationException(Response.status(403).build());
}
account = new Account();
account.setNumber(number);
account.setAuthenticationCredentials(new AuthenticationCredentials(password));
account.setSignalingKey(accountAttributes.getSignalingKey());
account.setSupportsSms(accountAttributes.getSupportsSms());
account.setFetchesMessages(accountAttributes.getFetchesMessages());
accounts.createAccountOnExistingNumber(account);
pendingDevices.remove(number);
logger.debug("Stored new device account...");
} catch (InvalidAuthorizationHeaderException e) {
logger.info("Bad Authorization Header", e);
throw new WebApplicationException(Response.status(401).build());
}
return account.getDeviceId();
}
}