parent
b8fb8a52f1
commit
f5aec1c894
|
@ -41,6 +41,7 @@ import org.whispersystems.textsecuregcm.controllers.FederationControllerV2;
|
||||||
import org.whispersystems.textsecuregcm.controllers.KeepAliveController;
|
import org.whispersystems.textsecuregcm.controllers.KeepAliveController;
|
||||||
import org.whispersystems.textsecuregcm.controllers.KeysController;
|
import org.whispersystems.textsecuregcm.controllers.KeysController;
|
||||||
import org.whispersystems.textsecuregcm.controllers.MessageController;
|
import org.whispersystems.textsecuregcm.controllers.MessageController;
|
||||||
|
import org.whispersystems.textsecuregcm.controllers.ProfileController;
|
||||||
import org.whispersystems.textsecuregcm.controllers.ProvisioningController;
|
import org.whispersystems.textsecuregcm.controllers.ProvisioningController;
|
||||||
import org.whispersystems.textsecuregcm.controllers.ReceiptController;
|
import org.whispersystems.textsecuregcm.controllers.ReceiptController;
|
||||||
import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
|
import org.whispersystems.textsecuregcm.federation.FederatedClientManager;
|
||||||
|
@ -196,6 +197,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
AttachmentController attachmentController = new AttachmentController(rateLimiters, federatedClientManager, urlSigner);
|
AttachmentController attachmentController = new AttachmentController(rateLimiters, federatedClientManager, urlSigner);
|
||||||
KeysController keysController = new KeysController(rateLimiters, keys, accountsManager, federatedClientManager);
|
KeysController keysController = new KeysController(rateLimiters, keys, accountsManager, federatedClientManager);
|
||||||
MessageController messageController = new MessageController(rateLimiters, pushSender, receiptSender, accountsManager, messagesManager, federatedClientManager);
|
MessageController messageController = new MessageController(rateLimiters, pushSender, receiptSender, accountsManager, messagesManager, federatedClientManager);
|
||||||
|
ProfileController profileController = new ProfileController(rateLimiters , accountsManager);
|
||||||
|
|
||||||
environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<Account>()
|
environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<Account>()
|
||||||
.setAuthenticator(deviceAuthenticator)
|
.setAuthenticator(deviceAuthenticator)
|
||||||
|
@ -217,6 +219,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
environment.jersey().register(attachmentController);
|
environment.jersey().register(attachmentController);
|
||||||
environment.jersey().register(keysController);
|
environment.jersey().register(keysController);
|
||||||
environment.jersey().register(messageController);
|
environment.jersey().register(messageController);
|
||||||
|
environment.jersey().register(profileController);
|
||||||
|
|
||||||
///
|
///
|
||||||
WebSocketEnvironment webSocketEnvironment = new WebSocketEnvironment(environment, config.getWebSocketConfiguration(), 90000);
|
WebSocketEnvironment webSocketEnvironment = new WebSocketEnvironment(environment, config.getWebSocketConfiguration(), 90000);
|
||||||
|
@ -224,6 +227,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
webSocketEnvironment.setConnectListener(new AuthenticatedConnectListener(accountsManager, pushSender, receiptSender, messagesManager, pubSubManager));
|
webSocketEnvironment.setConnectListener(new AuthenticatedConnectListener(accountsManager, pushSender, receiptSender, messagesManager, pubSubManager));
|
||||||
webSocketEnvironment.jersey().register(new KeepAliveController(pubSubManager));
|
webSocketEnvironment.jersey().register(new KeepAliveController(pubSubManager));
|
||||||
webSocketEnvironment.jersey().register(messageController);
|
webSocketEnvironment.jersey().register(messageController);
|
||||||
|
webSocketEnvironment.jersey().register(profileController);
|
||||||
|
|
||||||
WebSocketEnvironment provisioningEnvironment = new WebSocketEnvironment(environment, webSocketEnvironment.getRequestLog(), 60000);
|
WebSocketEnvironment provisioningEnvironment = new WebSocketEnvironment(environment, webSocketEnvironment.getRequestLog(), 60000);
|
||||||
provisioningEnvironment.setConnectListener(new ProvisioningConnectListener(pubSubManager));
|
provisioningEnvironment.setConnectListener(new ProvisioningConnectListener(pubSubManager));
|
||||||
|
|
|
@ -53,6 +53,9 @@ public class RateLimitsConfiguration {
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private RateLimitConfiguration turnAllocations = new RateLimitConfiguration(60, 60);
|
private RateLimitConfiguration turnAllocations = new RateLimitConfiguration(60, 60);
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private RateLimitConfiguration profile = new RateLimitConfiguration(4320, 3);
|
||||||
|
|
||||||
public RateLimitConfiguration getAllocateDevice() {
|
public RateLimitConfiguration getAllocateDevice() {
|
||||||
return allocateDevice;
|
return allocateDevice;
|
||||||
}
|
}
|
||||||
|
@ -97,6 +100,10 @@ public class RateLimitsConfiguration {
|
||||||
return turnAllocations;
|
return turnAllocations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RateLimitConfiguration getProfile() {
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
|
||||||
public static class RateLimitConfiguration {
|
public static class RateLimitConfiguration {
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private int bucketSize;
|
private int bucketSize;
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
package org.whispersystems.textsecuregcm.controllers;
|
||||||
|
|
||||||
|
import com.codahale.metrics.annotation.Timed;
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
import org.whispersystems.textsecuregcm.entities.Profile;
|
||||||
|
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
|
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.PathParam;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import javax.ws.rs.WebApplicationException;
|
||||||
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Response;
|
||||||
|
|
||||||
|
import io.dropwizard.auth.Auth;
|
||||||
|
|
||||||
|
@Path("/v1/profile")
|
||||||
|
public class ProfileController {
|
||||||
|
|
||||||
|
private final RateLimiters rateLimiters;
|
||||||
|
private final AccountsManager accountsManager;
|
||||||
|
|
||||||
|
public ProfileController(RateLimiters rateLimiters, AccountsManager accountsManager) {
|
||||||
|
this.rateLimiters = rateLimiters;
|
||||||
|
this.accountsManager = accountsManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Timed
|
||||||
|
@GET
|
||||||
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
|
@Path("/{number}")
|
||||||
|
public Profile getProfile(@Auth Account account, @PathParam("number") String number)
|
||||||
|
throws RateLimitExceededException
|
||||||
|
{
|
||||||
|
rateLimiters.getProfileLimiter().validate(account.getNumber());
|
||||||
|
|
||||||
|
Optional<Account> accountProfile = accountsManager.get(number);
|
||||||
|
|
||||||
|
if (!accountProfile.isPresent()) {
|
||||||
|
throw new WebApplicationException(Response.status(404).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Profile(accountProfile.get().getIdentityKey());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package org.whispersystems.textsecuregcm.entities;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
|
||||||
|
public class Profile {
|
||||||
|
|
||||||
|
@JsonProperty
|
||||||
|
private String identityKey;
|
||||||
|
|
||||||
|
public Profile() {}
|
||||||
|
|
||||||
|
public Profile(String identityKey) {
|
||||||
|
this.identityKey = identityKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public String getIdentityKey() {
|
||||||
|
return identityKey;
|
||||||
|
}
|
||||||
|
}
|
|
@ -38,6 +38,8 @@ public class RateLimiters {
|
||||||
|
|
||||||
private final RateLimiter turnLimiter;
|
private final RateLimiter turnLimiter;
|
||||||
|
|
||||||
|
private final RateLimiter profileLimiter;
|
||||||
|
|
||||||
public RateLimiters(RateLimitsConfiguration config, JedisPool cacheClient) {
|
public RateLimiters(RateLimitsConfiguration config, JedisPool cacheClient) {
|
||||||
this.smsDestinationLimiter = new RateLimiter(cacheClient, "smsDestination",
|
this.smsDestinationLimiter = new RateLimiter(cacheClient, "smsDestination",
|
||||||
config.getSmsDestination().getBucketSize(),
|
config.getSmsDestination().getBucketSize(),
|
||||||
|
@ -82,6 +84,10 @@ public class RateLimiters {
|
||||||
this.turnLimiter = new RateLimiter(cacheClient, "turnAllocate",
|
this.turnLimiter = new RateLimiter(cacheClient, "turnAllocate",
|
||||||
config.getTurnAllocations().getBucketSize(),
|
config.getTurnAllocations().getBucketSize(),
|
||||||
config.getTurnAllocations().getLeakRatePerMinute());
|
config.getTurnAllocations().getLeakRatePerMinute());
|
||||||
|
|
||||||
|
this.profileLimiter = new RateLimiter(cacheClient, "profile",
|
||||||
|
config.getProfile().getBucketSize(),
|
||||||
|
config.getProfile().getLeakRatePerMinute());
|
||||||
}
|
}
|
||||||
|
|
||||||
public RateLimiter getAllocateDeviceLimiter() {
|
public RateLimiter getAllocateDeviceLimiter() {
|
||||||
|
@ -128,4 +134,8 @@ public class RateLimiters {
|
||||||
return turnLimiter;
|
return turnLimiter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public RateLimiter getProfileLimiter() {
|
||||||
|
return profileLimiter;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -145,7 +145,11 @@ public class APNSender implements Managed {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (registrationId.equals(device.get().getApnId())) {
|
||||||
logger.info("APN Unregister APN ID matches! " + number + ", " + deviceId);
|
logger.info("APN Unregister APN ID matches! " + number + ", " + deviceId);
|
||||||
|
} else if (registrationId.equals(device.get().getVoipApnId())) {
|
||||||
|
logger.info("APN Unregister VoIP ID matches! " + number + ", " + deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
long tokenTimestamp = device.get().getPushTimestamp();
|
long tokenTimestamp = device.get().getPushTimestamp();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
package org.whispersystems.textsecuregcm.tests.controllers;
|
||||||
|
|
||||||
|
import com.google.common.base.Optional;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.whispersystems.textsecuregcm.controllers.ProfileController;
|
||||||
|
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
||||||
|
import org.whispersystems.textsecuregcm.entities.Profile;
|
||||||
|
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||||
|
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
public class ProfileControllerTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProfileGet() throws RateLimitExceededException {
|
||||||
|
Account requestAccount = mock(Account.class );
|
||||||
|
Account profileAccount = mock(Account.class );
|
||||||
|
RateLimiter rateLimiter = mock(RateLimiter.class );
|
||||||
|
RateLimiters rateLimiters = mock(RateLimiters.class );
|
||||||
|
AccountsManager accountsManager = mock(AccountsManager.class);
|
||||||
|
|
||||||
|
when(rateLimiters.getProfileLimiter()).thenReturn(rateLimiter);
|
||||||
|
when(requestAccount.getNumber()).thenReturn("foo");
|
||||||
|
when(profileAccount.getIdentityKey()).thenReturn("bar");
|
||||||
|
when(accountsManager.get(eq("baz"))).thenReturn(Optional.of(profileAccount));
|
||||||
|
|
||||||
|
ProfileController profileController = new ProfileController(rateLimiters, accountsManager);
|
||||||
|
Profile result = profileController.getProfile(requestAccount, "baz");
|
||||||
|
|
||||||
|
assertEquals(result.getIdentityKey(), "bar");
|
||||||
|
|
||||||
|
verify(accountsManager, times(1)).get(eq("baz"));
|
||||||
|
verify(rateLimiters, times(1)).getProfileLimiter();
|
||||||
|
verify(rateLimiter, times(1)).validate("foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue