Support for signature token based account verification.
This commit is contained in:
parent
8f2722263f
commit
222c7ea641
|
@ -25,6 +25,7 @@ import org.whispersystems.textsecuregcm.configuration.MemcacheConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.MetricsConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.MetricsConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.NexmoConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.NexmoConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.RateLimitsConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.RedPhoneConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.RedisConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.RedisConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.S3Configuration;
|
import org.whispersystems.textsecuregcm.configuration.S3Configuration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.TwilioConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.TwilioConfiguration;
|
||||||
|
@ -95,6 +96,9 @@ public class WhisperServerConfiguration extends Configuration {
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private WebsocketConfiguration websocket = new WebsocketConfiguration();
|
private WebsocketConfiguration websocket = new WebsocketConfiguration();
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private RedPhoneConfiguration redphone = new RedPhoneConfiguration();
|
||||||
|
|
||||||
public WebsocketConfiguration getWebsocketConfiguration() {
|
public WebsocketConfiguration getWebsocketConfiguration() {
|
||||||
return websocket;
|
return websocket;
|
||||||
}
|
}
|
||||||
|
@ -146,4 +150,8 @@ public class WhisperServerConfiguration extends Configuration {
|
||||||
public MetricsConfiguration getMetricsConfiguration() {
|
public MetricsConfiguration getMetricsConfiguration() {
|
||||||
return viz;
|
return viz;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RedPhoneConfiguration getRedphoneConfiguration() {
|
||||||
|
return redphone;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ import org.whispersystems.textsecuregcm.providers.MemcacheHealthCheck;
|
||||||
import org.whispersystems.textsecuregcm.providers.MemcachedClientFactory;
|
import org.whispersystems.textsecuregcm.providers.MemcachedClientFactory;
|
||||||
import org.whispersystems.textsecuregcm.providers.RedisClientFactory;
|
import org.whispersystems.textsecuregcm.providers.RedisClientFactory;
|
||||||
import org.whispersystems.textsecuregcm.providers.RedisHealthCheck;
|
import org.whispersystems.textsecuregcm.providers.RedisHealthCheck;
|
||||||
|
import org.whispersystems.textsecuregcm.providers.TimeProvider;
|
||||||
import org.whispersystems.textsecuregcm.push.APNSender;
|
import org.whispersystems.textsecuregcm.push.APNSender;
|
||||||
import org.whispersystems.textsecuregcm.push.GCMSender;
|
import org.whispersystems.textsecuregcm.push.GCMSender;
|
||||||
import org.whispersystems.textsecuregcm.push.PushSender;
|
import org.whispersystems.textsecuregcm.push.PushSender;
|
||||||
|
@ -163,6 +164,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
SmsSender smsSender = new SmsSender(twilioSmsSender, nexmoSmsSender, config.getTwilioConfiguration().isInternational());
|
SmsSender smsSender = new SmsSender(twilioSmsSender, nexmoSmsSender, config.getTwilioConfiguration().isInternational());
|
||||||
UrlSigner urlSigner = new UrlSigner(config.getS3Configuration());
|
UrlSigner urlSigner = new UrlSigner(config.getS3Configuration());
|
||||||
PushSender pushSender = new PushSender(gcmSender, apnSender, websocketSender);
|
PushSender pushSender = new PushSender(gcmSender, apnSender, websocketSender);
|
||||||
|
Optional<byte[]> authorizationKey = config.getRedphoneConfiguration().getAuthorizationKey();
|
||||||
|
|
||||||
AttachmentController attachmentController = new AttachmentController(rateLimiters, federatedClientManager, urlSigner);
|
AttachmentController attachmentController = new AttachmentController(rateLimiters, federatedClientManager, urlSigner);
|
||||||
KeysControllerV1 keysControllerV1 = new KeysControllerV1(rateLimiters, keys, accountsManager, federatedClientManager);
|
KeysControllerV1 keysControllerV1 = new KeysControllerV1(rateLimiters, keys, accountsManager, federatedClientManager);
|
||||||
|
@ -174,7 +176,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
deviceAuthenticator,
|
deviceAuthenticator,
|
||||||
Device.class, "WhisperServer"));
|
Device.class, "WhisperServer"));
|
||||||
|
|
||||||
environment.jersey().register(new AccountController(pendingAccountsManager, accountsManager, rateLimiters, smsSender, storedMessages));
|
environment.jersey().register(new AccountController(pendingAccountsManager, accountsManager, rateLimiters, smsSender, storedMessages, new TimeProvider(), authorizationKey));
|
||||||
environment.jersey().register(new DeviceController(pendingDevicesManager, accountsManager, rateLimiters));
|
environment.jersey().register(new DeviceController(pendingDevicesManager, accountsManager, rateLimiters));
|
||||||
environment.jersey().register(new DirectoryController(rateLimiters, directory));
|
environment.jersey().register(new DirectoryController(rateLimiters, directory));
|
||||||
environment.jersey().register(new FederationControllerV1(accountsManager, attachmentController, messageController, keysControllerV1));
|
environment.jersey().register(new FederationControllerV1(accountsManager, attachmentController, messageController, keysControllerV1));
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
package org.whispersystems.textsecuregcm.auth;
|
||||||
|
|
||||||
|
|
||||||
|
import org.apache.commons.codec.DecoderException;
|
||||||
|
import org.apache.commons.codec.binary.Hex;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.whispersystems.textsecuregcm.util.Util;
|
||||||
|
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
public class AuthorizationToken {
|
||||||
|
|
||||||
|
private final Logger logger = LoggerFactory.getLogger(AuthorizationToken.class);
|
||||||
|
|
||||||
|
private final String token;
|
||||||
|
private final byte[] key;
|
||||||
|
|
||||||
|
public AuthorizationToken(String token, byte[] key) {
|
||||||
|
this.token = token;
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isValid(String number, long currentTimeMillis) {
|
||||||
|
String[] parts = token.split(":");
|
||||||
|
|
||||||
|
if (parts.length != 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!number.equals(parts[0])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValidTime(parts[1], currentTimeMillis)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValidSignature(parts[0] + ":" + parts[1], parts[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidTime(String timeString, long currentTimeMillis) {
|
||||||
|
try {
|
||||||
|
long tokenTime = Long.parseLong(timeString);
|
||||||
|
long ourTime = TimeUnit.MILLISECONDS.toSeconds(currentTimeMillis);
|
||||||
|
|
||||||
|
return TimeUnit.SECONDS.toHours(Math.abs(ourTime - tokenTime)) < 24;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
logger.warn("Number Format", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValidSignature(String prefix, String suffix) {
|
||||||
|
try {
|
||||||
|
Mac hmac = Mac.getInstance("HmacSHA256");
|
||||||
|
hmac.init(new SecretKeySpec(key, "HmacSHA256"));
|
||||||
|
|
||||||
|
byte[] ourSuffix = Util.truncate(hmac.doFinal(prefix.getBytes()), 10);
|
||||||
|
byte[] theirSuffix = Hex.decodeHex(suffix.toCharArray());
|
||||||
|
|
||||||
|
return MessageDigest.isEqual(ourSuffix, theirSuffix);
|
||||||
|
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
} catch (DecoderException e) {
|
||||||
|
logger.warn("Authorizationtoken", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
import org.apache.commons.codec.DecoderException;
|
||||||
|
import org.apache.commons.codec.binary.Hex;
|
||||||
|
|
||||||
|
public class RedPhoneConfiguration {
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String authKey;
|
||||||
|
|
||||||
|
public Optional<byte[]> getAuthorizationKey() throws DecoderException {
|
||||||
|
if (authKey == null || authKey.trim().length() == 0) {
|
||||||
|
return Optional.absent();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Optional.of(Hex.decodeHex(authKey.toCharArray()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,7 +27,9 @@ import org.whispersystems.textsecuregcm.auth.InvalidAuthorizationHeaderException
|
||||||
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
||||||
import org.whispersystems.textsecuregcm.entities.ApnRegistrationId;
|
import org.whispersystems.textsecuregcm.entities.ApnRegistrationId;
|
||||||
import org.whispersystems.textsecuregcm.entities.GcmRegistrationId;
|
import org.whispersystems.textsecuregcm.entities.GcmRegistrationId;
|
||||||
|
import org.whispersystems.textsecuregcm.auth.AuthorizationToken;
|
||||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||||
|
import org.whispersystems.textsecuregcm.providers.TimeProvider;
|
||||||
import org.whispersystems.textsecuregcm.sms.SmsSender;
|
import org.whispersystems.textsecuregcm.sms.SmsSender;
|
||||||
import org.whispersystems.textsecuregcm.sms.TwilioSmsSender;
|
import org.whispersystems.textsecuregcm.sms.TwilioSmsSender;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
|
@ -68,18 +70,24 @@ public class AccountController {
|
||||||
private final RateLimiters rateLimiters;
|
private final RateLimiters rateLimiters;
|
||||||
private final SmsSender smsSender;
|
private final SmsSender smsSender;
|
||||||
private final StoredMessages storedMessages;
|
private final StoredMessages storedMessages;
|
||||||
|
private final TimeProvider timeProvider;
|
||||||
|
private final Optional<byte[]> authorizationKey;
|
||||||
|
|
||||||
public AccountController(PendingAccountsManager pendingAccounts,
|
public AccountController(PendingAccountsManager pendingAccounts,
|
||||||
AccountsManager accounts,
|
AccountsManager accounts,
|
||||||
RateLimiters rateLimiters,
|
RateLimiters rateLimiters,
|
||||||
SmsSender smsSenderFactory,
|
SmsSender smsSenderFactory,
|
||||||
StoredMessages storedMessages)
|
StoredMessages storedMessages,
|
||||||
|
TimeProvider timeProvider,
|
||||||
|
Optional<byte[]> authorizationKey)
|
||||||
{
|
{
|
||||||
this.pendingAccounts = pendingAccounts;
|
this.pendingAccounts = pendingAccounts;
|
||||||
this.accounts = accounts;
|
this.accounts = accounts;
|
||||||
this.rateLimiters = rateLimiters;
|
this.rateLimiters = rateLimiters;
|
||||||
this.smsSender = smsSenderFactory;
|
this.smsSender = smsSenderFactory;
|
||||||
this.storedMessages = storedMessages;
|
this.storedMessages = storedMessages;
|
||||||
|
this.timeProvider = timeProvider;
|
||||||
|
this.authorizationKey = authorizationKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Timed
|
@Timed
|
||||||
|
@ -145,30 +153,46 @@ public class AccountController {
|
||||||
throw new WebApplicationException(Response.status(417).build());
|
throw new WebApplicationException(Response.status(417).build());
|
||||||
}
|
}
|
||||||
|
|
||||||
Device device = new Device();
|
createAccount(number, password, accountAttributes);
|
||||||
device.setId(Device.MASTER_ID);
|
|
||||||
device.setAuthenticationCredentials(new AuthenticationCredentials(password));
|
|
||||||
device.setSignalingKey(accountAttributes.getSignalingKey());
|
|
||||||
device.setFetchesMessages(accountAttributes.getFetchesMessages());
|
|
||||||
device.setRegistrationId(accountAttributes.getRegistrationId());
|
|
||||||
|
|
||||||
Account account = new Account();
|
|
||||||
account.setNumber(number);
|
|
||||||
account.setSupportsSms(accountAttributes.getSupportsSms());
|
|
||||||
account.addDevice(device);
|
|
||||||
|
|
||||||
accounts.create(account);
|
|
||||||
storedMessages.clear(new WebsocketAddress(number, Device.MASTER_ID));
|
|
||||||
pendingAccounts.remove(number);
|
|
||||||
|
|
||||||
logger.debug("Stored device...");
|
|
||||||
} 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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Timed
|
||||||
|
@PUT
|
||||||
|
@Consumes(MediaType.APPLICATION_JSON)
|
||||||
|
@Path("/token/{verification_token}")
|
||||||
|
public void verifyToken(@PathParam("verification_token") String verificationToken,
|
||||||
|
@HeaderParam("Authorization") String authorizationHeader,
|
||||||
|
@Valid AccountAttributes accountAttributes)
|
||||||
|
throws RateLimitExceededException
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
AuthorizationHeader header = AuthorizationHeader.fromFullHeader(authorizationHeader);
|
||||||
|
String number = header.getNumber();
|
||||||
|
String password = header.getPassword();
|
||||||
|
|
||||||
|
rateLimiters.getVerifyLimiter().validate(number);
|
||||||
|
|
||||||
|
if (!authorizationKey.isPresent()) {
|
||||||
|
logger.debug("Attempt to authorize with key but not configured...");
|
||||||
|
throw new WebApplicationException(Response.status(403).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthorizationToken token = new AuthorizationToken(verificationToken, authorizationKey.get());
|
||||||
|
|
||||||
|
if (!token.isValid(number, timeProvider.getCurrentTimeMillis())) {
|
||||||
|
throw new WebApplicationException(Response.status(403).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
createAccount(number, password, accountAttributes);
|
||||||
|
} catch (InvalidAuthorizationHeaderException e) {
|
||||||
|
logger.info("Bad authorization header", e);
|
||||||
|
throw new WebApplicationException(Response.status(401).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Timed
|
@Timed
|
||||||
@PUT
|
@PUT
|
||||||
|
@ -219,6 +243,26 @@ public class AccountController {
|
||||||
encodedVerificationText)).build();
|
encodedVerificationText)).build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void createAccount(String number, String password, AccountAttributes accountAttributes) {
|
||||||
|
Device device = new Device();
|
||||||
|
device.setId(Device.MASTER_ID);
|
||||||
|
device.setAuthenticationCredentials(new AuthenticationCredentials(password));
|
||||||
|
device.setSignalingKey(accountAttributes.getSignalingKey());
|
||||||
|
device.setFetchesMessages(accountAttributes.getFetchesMessages());
|
||||||
|
device.setRegistrationId(accountAttributes.getRegistrationId());
|
||||||
|
|
||||||
|
Account account = new Account();
|
||||||
|
account.setNumber(number);
|
||||||
|
account.setSupportsSms(accountAttributes.getSupportsSms());
|
||||||
|
account.addDevice(device);
|
||||||
|
|
||||||
|
accounts.create(account);
|
||||||
|
storedMessages.clear(new WebsocketAddress(number, Device.MASTER_ID));
|
||||||
|
pendingAccounts.remove(number);
|
||||||
|
|
||||||
|
logger.debug("Stored device...");
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting protected VerificationCode generateVerificationCode() {
|
@VisibleForTesting protected VerificationCode generateVerificationCode() {
|
||||||
try {
|
try {
|
||||||
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
|
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package org.whispersystems.textsecuregcm.providers;
|
||||||
|
|
||||||
|
public class TimeProvider {
|
||||||
|
public long getCurrentTimeMillis() {
|
||||||
|
return System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,8 @@ package org.whispersystems.textsecuregcm.tests.controllers;
|
||||||
|
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
import com.sun.jersey.api.client.ClientResponse;
|
import com.sun.jersey.api.client.ClientResponse;
|
||||||
|
import org.apache.commons.codec.DecoderException;
|
||||||
|
import org.apache.commons.codec.binary.Hex;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -9,6 +11,7 @@ import org.whispersystems.textsecuregcm.controllers.AccountController;
|
||||||
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
|
||||||
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.providers.TimeProvider;
|
||||||
import org.whispersystems.textsecuregcm.sms.SmsSender;
|
import org.whispersystems.textsecuregcm.sms.SmsSender;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
|
@ -33,6 +36,8 @@ public class AccountControllerTest {
|
||||||
private RateLimiter rateLimiter = mock(RateLimiter.class );
|
private RateLimiter rateLimiter = mock(RateLimiter.class );
|
||||||
private SmsSender smsSender = mock(SmsSender.class );
|
private SmsSender smsSender = mock(SmsSender.class );
|
||||||
private StoredMessages storedMessages = mock(StoredMessages.class );
|
private StoredMessages storedMessages = mock(StoredMessages.class );
|
||||||
|
private TimeProvider timeProvider = mock(TimeProvider.class );
|
||||||
|
private static byte[] authorizationKey = decodeHex("3a078586eea8971155f5c1ebd73c8c923cbec1c3ed22a54722e4e88321dc749f");
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public final ResourceTestRule resources = ResourceTestRule.builder()
|
public final ResourceTestRule resources = ResourceTestRule.builder()
|
||||||
|
@ -41,7 +46,9 @@ public class AccountControllerTest {
|
||||||
accountsManager,
|
accountsManager,
|
||||||
rateLimiters,
|
rateLimiters,
|
||||||
smsSender,
|
smsSender,
|
||||||
storedMessages))
|
storedMessages,
|
||||||
|
timeProvider,
|
||||||
|
Optional.of(authorizationKey)))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
@ -51,6 +58,8 @@ public class AccountControllerTest {
|
||||||
when(rateLimiters.getVoiceDestinationLimiter()).thenReturn(rateLimiter);
|
when(rateLimiters.getVoiceDestinationLimiter()).thenReturn(rateLimiter);
|
||||||
when(rateLimiters.getVerifyLimiter()).thenReturn(rateLimiter);
|
when(rateLimiters.getVerifyLimiter()).thenReturn(rateLimiter);
|
||||||
|
|
||||||
|
when(timeProvider.getCurrentTimeMillis()).thenReturn(System.currentTimeMillis());
|
||||||
|
|
||||||
when(pendingAccountsManager.getCodeForNumber(SENDER)).thenReturn(Optional.of("1234"));
|
when(pendingAccountsManager.getCodeForNumber(SENDER)).thenReturn(Optional.of("1234"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,4 +102,84 @@ public class AccountControllerTest {
|
||||||
verifyNoMoreInteractions(accountsManager);
|
verifyNoMoreInteractions(accountsManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVerifyToken() throws Exception {
|
||||||
|
when(timeProvider.getCurrentTimeMillis()).thenReturn(1415917053106L);
|
||||||
|
|
||||||
|
String token = SENDER + ":1415906573:af4f046107c21721224a";
|
||||||
|
|
||||||
|
ClientResponse response =
|
||||||
|
resources.client().resource(String.format("/v1/accounts/token/%s", token))
|
||||||
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER, "bar"))
|
||||||
|
.entity(new AccountAttributes("keykeykeykey", false, false, 4444))
|
||||||
|
.type(MediaType.APPLICATION_JSON_TYPE)
|
||||||
|
.put(ClientResponse.class);
|
||||||
|
|
||||||
|
assertThat(response.getStatus()).isEqualTo(204);
|
||||||
|
|
||||||
|
verify(accountsManager, times(1)).create(isA(Account.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVerifyBadToken() throws Exception {
|
||||||
|
when(timeProvider.getCurrentTimeMillis()).thenReturn(1415917053106L);
|
||||||
|
|
||||||
|
String token = SENDER + ":1415906574:af4f046107c21721224a";
|
||||||
|
|
||||||
|
ClientResponse response =
|
||||||
|
resources.client().resource(String.format("/v1/accounts/token/%s", token))
|
||||||
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER, "bar"))
|
||||||
|
.entity(new AccountAttributes("keykeykeykey", false, false, 4444))
|
||||||
|
.type(MediaType.APPLICATION_JSON_TYPE)
|
||||||
|
.put(ClientResponse.class);
|
||||||
|
|
||||||
|
assertThat(response.getStatus()).isEqualTo(403);
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(accountsManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVerifyWrongToken() throws Exception {
|
||||||
|
when(timeProvider.getCurrentTimeMillis()).thenReturn(1415917053106L);
|
||||||
|
|
||||||
|
String token = SENDER + ":1415906573:af4f046107c21721224a";
|
||||||
|
|
||||||
|
ClientResponse response =
|
||||||
|
resources.client().resource(String.format("/v1/accounts/token/%s", token))
|
||||||
|
.header("Authorization", AuthHelper.getAuthHeader("+14151111111", "bar"))
|
||||||
|
.entity(new AccountAttributes("keykeykeykey", false, false, 4444))
|
||||||
|
.type(MediaType.APPLICATION_JSON_TYPE)
|
||||||
|
.put(ClientResponse.class);
|
||||||
|
|
||||||
|
assertThat(response.getStatus()).isEqualTo(403);
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(accountsManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVerifyExpiredToken() throws Exception {
|
||||||
|
when(timeProvider.getCurrentTimeMillis()).thenReturn(1416003757901L);
|
||||||
|
|
||||||
|
String token = SENDER + ":1415906573:af4f046107c21721224a";
|
||||||
|
|
||||||
|
ClientResponse response =
|
||||||
|
resources.client().resource(String.format("/v1/accounts/token/%s", token))
|
||||||
|
.header("Authorization", AuthHelper.getAuthHeader(SENDER, "bar"))
|
||||||
|
.entity(new AccountAttributes("keykeykeykey", false, false, 4444))
|
||||||
|
.type(MediaType.APPLICATION_JSON_TYPE)
|
||||||
|
.put(ClientResponse.class);
|
||||||
|
|
||||||
|
assertThat(response.getStatus()).isEqualTo(403);
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(accountsManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] decodeHex(String hex) {
|
||||||
|
try {
|
||||||
|
return Hex.decodeHex(hex.toCharArray());
|
||||||
|
} catch (DecoderException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue