mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-25 02:08:04 +01:00
Dropwizard 9 compatibility!
// FREEBIE
This commit is contained in:
@@ -20,15 +20,16 @@ import com.codahale.metrics.SharedMetricRegistries;
|
||||
import com.codahale.metrics.graphite.GraphiteReporter;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.google.common.base.Optional;
|
||||
import com.sun.jersey.api.client.Client;
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.eclipse.jetty.servlets.CrossOriginFilter;
|
||||
import org.skife.jdbi.v2.DBI;
|
||||
import org.whispersystems.dispatch.DispatchChannel;
|
||||
import org.whispersystems.dispatch.DispatchManager;
|
||||
import org.whispersystems.dropwizard.simpleauth.AuthDynamicFeature;
|
||||
import org.whispersystems.dropwizard.simpleauth.AuthValueFactoryProvider;
|
||||
import org.whispersystems.dropwizard.simpleauth.BasicCredentialAuthFilter;
|
||||
import org.whispersystems.textsecuregcm.auth.AccountAuthenticator;
|
||||
import org.whispersystems.textsecuregcm.auth.FederatedPeerAuthenticator;
|
||||
import org.whispersystems.textsecuregcm.auth.MultiBasicAuthProvider;
|
||||
import org.whispersystems.textsecuregcm.configuration.NexmoConfiguration;
|
||||
import org.whispersystems.textsecuregcm.controllers.AccountController;
|
||||
import org.whispersystems.textsecuregcm.controllers.AttachmentController;
|
||||
@@ -66,9 +67,9 @@ import org.whispersystems.textsecuregcm.push.WebsocketSender;
|
||||
import org.whispersystems.textsecuregcm.sms.NexmoSmsSender;
|
||||
import org.whispersystems.textsecuregcm.sms.SmsSender;
|
||||
import org.whispersystems.textsecuregcm.sms.TwilioSmsSender;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.Accounts;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.storage.DirectoryManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Keys;
|
||||
import org.whispersystems.textsecuregcm.storage.Messages;
|
||||
@@ -92,6 +93,7 @@ import org.whispersystems.websocket.setup.WebSocketEnvironment;
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.servlet.FilterRegistration;
|
||||
import javax.servlet.ServletRegistration;
|
||||
import javax.ws.rs.client.Client;
|
||||
import java.security.Security;
|
||||
import java.util.EnumSet;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -159,19 +161,20 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
Client httpClient = new JerseyClientBuilder(environment).using(config.getJerseyClientConfiguration())
|
||||
.build(getName());
|
||||
|
||||
DirectoryManager directory = new DirectoryManager(directoryClient);
|
||||
PendingAccountsManager pendingAccountsManager = new PendingAccountsManager(pendingAccounts, cacheClient);
|
||||
PendingDevicesManager pendingDevicesManager = new PendingDevicesManager (pendingDevices, cacheClient);
|
||||
AccountsManager accountsManager = new AccountsManager(accounts, directory, cacheClient);
|
||||
FederatedClientManager federatedClientManager = new FederatedClientManager(config.getFederationConfiguration());
|
||||
MessagesManager messagesManager = new MessagesManager(messages);
|
||||
DeadLetterHandler deadLetterHandler = new DeadLetterHandler(messagesManager);
|
||||
DispatchManager dispatchManager = new DispatchManager(cacheClientFactory, Optional.<DispatchChannel>of(deadLetterHandler));
|
||||
PubSubManager pubSubManager = new PubSubManager(cacheClient, dispatchManager);
|
||||
PushServiceClient pushServiceClient = new PushServiceClient(httpClient, config.getPushConfiguration());
|
||||
WebsocketSender websocketSender = new WebsocketSender(messagesManager, pubSubManager);
|
||||
AccountAuthenticator deviceAuthenticator = new AccountAuthenticator(accountsManager);
|
||||
RateLimiters rateLimiters = new RateLimiters(config.getLimitsConfiguration(), cacheClient);
|
||||
DirectoryManager directory = new DirectoryManager(directoryClient);
|
||||
PendingAccountsManager pendingAccountsManager = new PendingAccountsManager(pendingAccounts, cacheClient);
|
||||
PendingDevicesManager pendingDevicesManager = new PendingDevicesManager (pendingDevices, cacheClient );
|
||||
AccountsManager accountsManager = new AccountsManager(accounts, directory, cacheClient);
|
||||
FederatedClientManager federatedClientManager = new FederatedClientManager(environment, config.getJerseyClientConfiguration(), config.getFederationConfiguration());
|
||||
MessagesManager messagesManager = new MessagesManager(messages);
|
||||
DeadLetterHandler deadLetterHandler = new DeadLetterHandler(messagesManager);
|
||||
DispatchManager dispatchManager = new DispatchManager(cacheClientFactory, Optional.<DispatchChannel>of(deadLetterHandler));
|
||||
PubSubManager pubSubManager = new PubSubManager(cacheClient, dispatchManager);
|
||||
PushServiceClient pushServiceClient = new PushServiceClient(httpClient, config.getPushConfiguration());
|
||||
WebsocketSender websocketSender = new WebsocketSender(messagesManager, pubSubManager);
|
||||
AccountAuthenticator deviceAuthenticator = new AccountAuthenticator(accountsManager );
|
||||
FederatedPeerAuthenticator federatedPeerAuthenticator = new FederatedPeerAuthenticator(config.getFederationConfiguration());
|
||||
RateLimiters rateLimiters = new RateLimiters(config.getLimitsConfiguration(), cacheClient);
|
||||
|
||||
ApnFallbackManager apnFallbackManager = new ApnFallbackManager(pushServiceClient);
|
||||
TwilioSmsSender twilioSmsSender = new TwilioSmsSender(config.getTwilioConfiguration());
|
||||
@@ -192,10 +195,15 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||
KeysControllerV2 keysControllerV2 = new KeysControllerV2(rateLimiters, keys, accountsManager, federatedClientManager);
|
||||
MessageController messageController = new MessageController(rateLimiters, pushSender, receiptSender, accountsManager, messagesManager, federatedClientManager);
|
||||
|
||||
environment.jersey().register(new MultiBasicAuthProvider<>(new FederatedPeerAuthenticator(config.getFederationConfiguration()),
|
||||
FederatedPeer.class,
|
||||
deviceAuthenticator,
|
||||
Device.class, "WhisperServer"));
|
||||
environment.jersey().register(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<Account>()
|
||||
.setAuthenticator(deviceAuthenticator)
|
||||
.setPrincipal(Account.class)
|
||||
.buildAuthFilter(),
|
||||
new BasicCredentialAuthFilter.Builder<FederatedPeer>()
|
||||
.setAuthenticator(federatedPeerAuthenticator)
|
||||
.setPrincipal(FederatedPeer.class)
|
||||
.buildAuthFilter()));
|
||||
environment.jersey().register(new AuthValueFactoryProvider.Binder());
|
||||
|
||||
environment.jersey().register(new AccountController(pendingAccountsManager, accountsManager, rateLimiters, smsSender, messagesManager, new TimeProvider(), authorizationKey, config.getTestDevices()));
|
||||
environment.jersey().register(new DeviceController(pendingDevicesManager, accountsManager, rateLimiters));
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.codahale.metrics.SharedMetricRegistries;
|
||||
import com.google.common.base.Optional;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.dropwizard.simpleauth.Authenticator;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
@@ -29,7 +30,6 @@ import org.whispersystems.textsecuregcm.util.Constants;
|
||||
|
||||
import static com.codahale.metrics.MetricRegistry.name;
|
||||
import io.dropwizard.auth.AuthenticationException;
|
||||
import io.dropwizard.auth.Authenticator;
|
||||
import io.dropwizard.auth.basic.BasicCredentials;
|
||||
|
||||
public class AccountAuthenticator implements Authenticator<BasicCredentials, Account> {
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.codahale.metrics.SharedMetricRegistries;
|
||||
import com.google.common.base.Optional;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.dropwizard.simpleauth.Authenticator;
|
||||
import org.whispersystems.textsecuregcm.configuration.FederationConfiguration;
|
||||
import org.whispersystems.textsecuregcm.federation.FederatedPeer;
|
||||
import org.whispersystems.textsecuregcm.util.Constants;
|
||||
@@ -30,7 +31,6 @@ import java.util.List;
|
||||
|
||||
import static com.codahale.metrics.MetricRegistry.name;
|
||||
import io.dropwizard.auth.AuthenticationException;
|
||||
import io.dropwizard.auth.Authenticator;
|
||||
import io.dropwizard.auth.basic.BasicCredentials;
|
||||
|
||||
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
/**
|
||||
* Copyright (C) 2013 Open WhisperSystems
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.auth;
|
||||
|
||||
import com.sun.jersey.api.model.Parameter;
|
||||
import com.sun.jersey.core.spi.component.ComponentContext;
|
||||
import com.sun.jersey.core.spi.component.ComponentScope;
|
||||
import com.sun.jersey.spi.inject.Injectable;
|
||||
import com.sun.jersey.spi.inject.InjectableProvider;
|
||||
|
||||
import io.dropwizard.auth.Auth;
|
||||
import io.dropwizard.auth.Authenticator;
|
||||
import io.dropwizard.auth.basic.BasicAuthProvider;
|
||||
import io.dropwizard.auth.basic.BasicCredentials;
|
||||
|
||||
public class MultiBasicAuthProvider<T1,T2> implements InjectableProvider<Auth, Parameter> {
|
||||
|
||||
private final BasicAuthProvider<T1> provider1;
|
||||
private final BasicAuthProvider<T2> provider2;
|
||||
|
||||
private final Class<?> clazz1;
|
||||
private final Class<?> clazz2;
|
||||
|
||||
public MultiBasicAuthProvider(Authenticator<BasicCredentials, T1> authenticator1,
|
||||
Class<?> clazz1,
|
||||
Authenticator<BasicCredentials, T2> authenticator2,
|
||||
Class<?> clazz2,
|
||||
String realm)
|
||||
{
|
||||
this.provider1 = new BasicAuthProvider<>(authenticator1, realm);
|
||||
this.provider2 = new BasicAuthProvider<>(authenticator2, realm);
|
||||
this.clazz1 = clazz1;
|
||||
this.clazz2 = clazz2;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ComponentScope getScope() {
|
||||
return ComponentScope.PerRequest;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Injectable<?> getInjectable(ComponentContext componentContext,
|
||||
Auth auth, Parameter parameter)
|
||||
{
|
||||
if (parameter.getParameterClass().equals(clazz1)) {
|
||||
return this.provider1.getInjectable(componentContext, auth, parameter);
|
||||
} else {
|
||||
return this.provider2.getInjectable(componentContext, auth, parameter);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,8 +29,8 @@ public class KeepAliveController {
|
||||
|
||||
@Timed
|
||||
@GET
|
||||
public Response getKeepAlive(@Auth(required = false) Account account,
|
||||
@WebSocketSession WebSocketSessionContext context)
|
||||
public Response getKeepAlive(@Auth Account account,
|
||||
@WebSocketSession WebSocketSessionContext context)
|
||||
{
|
||||
if (account != null) {
|
||||
WebsocketAddress address = new WebsocketAddress(account.getNumber(),
|
||||
|
||||
@@ -52,7 +52,6 @@ public class KeysController {
|
||||
this.federatedClientManager = federatedClientManager;
|
||||
}
|
||||
|
||||
@Timed
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public PreKeyCount getStatus(@Auth Account account) {
|
||||
|
||||
@@ -17,20 +17,17 @@
|
||||
package org.whispersystems.textsecuregcm.federation;
|
||||
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
|
||||
import com.google.common.base.Optional;
|
||||
import com.sun.jersey.api.client.Client;
|
||||
import com.sun.jersey.api.client.ClientHandlerException;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.api.client.UniformInterfaceException;
|
||||
import com.sun.jersey.api.client.WebResource;
|
||||
import com.sun.jersey.api.client.config.ClientConfig;
|
||||
import com.sun.jersey.api.client.config.DefaultClientConfig;
|
||||
import com.sun.jersey.api.json.JSONConfiguration;
|
||||
import com.sun.jersey.client.urlconnection.HTTPSProperties;
|
||||
import org.apache.http.client.CredentialsProvider;
|
||||
import org.apache.http.config.Registry;
|
||||
import org.apache.http.config.RegistryBuilder;
|
||||
import org.apache.http.conn.socket.ConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.StrictHostnameVerifier;
|
||||
import org.bouncycastle.openssl.PEMReader;
|
||||
import org.glassfish.jersey.SslConfigurator;
|
||||
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.entities.AccountCount;
|
||||
@@ -40,11 +37,16 @@ import org.whispersystems.textsecuregcm.entities.ClientContacts;
|
||||
import org.whispersystems.textsecuregcm.entities.IncomingMessageList;
|
||||
import org.whispersystems.textsecuregcm.entities.PreKeyResponseV1;
|
||||
import org.whispersystems.textsecuregcm.entities.PreKeyResponseV2;
|
||||
import org.whispersystems.textsecuregcm.util.Base64;
|
||||
import org.whispersystems.textsecuregcm.providers.JacksonConfigurator;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManagerFactory;
|
||||
import javax.ws.rs.ProcessingException;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -59,7 +61,10 @@ import java.security.SecureRandom;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.dropwizard.client.JerseyClientBuilder;
|
||||
import io.dropwizard.client.JerseyClientConfiguration;
|
||||
import io.dropwizard.setup.Environment;
|
||||
|
||||
public class FederatedClient {
|
||||
|
||||
@@ -75,15 +80,14 @@ public class FederatedClient {
|
||||
|
||||
private final FederatedPeer peer;
|
||||
private final Client client;
|
||||
private final String authorizationHeader;
|
||||
|
||||
public FederatedClient(String federationName, FederatedPeer peer)
|
||||
public FederatedClient(Environment environment, JerseyClientConfiguration configuration,
|
||||
String federationName, FederatedPeer peer)
|
||||
throws IOException
|
||||
{
|
||||
try {
|
||||
this.client = Client.create(getClientConfig(peer));
|
||||
this.peer = peer;
|
||||
this.authorizationHeader = getAuthorizationHeader(federationName, peer);
|
||||
this.client = createClient(environment, configuration, federationName, peer);
|
||||
this.peer = peer;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (KeyStoreException | KeyManagementException | CertificateException e) {
|
||||
@@ -93,20 +97,14 @@ public class FederatedClient {
|
||||
|
||||
public URL getSignedAttachmentUri(long attachmentId) throws IOException {
|
||||
try {
|
||||
WebResource resource = client.resource(peer.getUrl())
|
||||
.path(String.format(ATTACHMENT_URI_PATH, attachmentId));
|
||||
AttachmentUri response = client.target(peer.getUrl())
|
||||
.path(String.format(ATTACHMENT_URI_PATH, attachmentId))
|
||||
.request()
|
||||
.accept(MediaType.APPLICATION_JSON_TYPE)
|
||||
.get(AttachmentUri.class);
|
||||
|
||||
ClientResponse response = resource.accept(MediaType.APPLICATION_JSON)
|
||||
.header("Authorization", authorizationHeader)
|
||||
.get(ClientResponse.class);
|
||||
|
||||
if (response.getStatus() < 200 || response.getStatus() >= 300) {
|
||||
throw new WebApplicationException(clientResponseToResponse(response));
|
||||
}
|
||||
|
||||
return response.getEntity(AttachmentUri.class).getLocation();
|
||||
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
return response.getLocation();
|
||||
} catch (ProcessingException e) {
|
||||
logger.warn("Bad URI", e);
|
||||
throw new IOException(e);
|
||||
}
|
||||
@@ -114,19 +112,14 @@ public class FederatedClient {
|
||||
|
||||
public Optional<PreKeyResponseV1> getKeysV1(String destination, String device) {
|
||||
try {
|
||||
WebResource resource = client.resource(peer.getUrl()).path(String.format(PREKEY_PATH_DEVICE_V1, destination, device));
|
||||
PreKeyResponseV1 response = client.target(peer.getUrl())
|
||||
.path(String.format(PREKEY_PATH_DEVICE_V1, destination, device))
|
||||
.request()
|
||||
.accept(MediaType.APPLICATION_JSON_TYPE)
|
||||
.get(PreKeyResponseV1.class);
|
||||
|
||||
ClientResponse response = resource.accept(MediaType.APPLICATION_JSON)
|
||||
.header("Authorization", authorizationHeader)
|
||||
.get(ClientResponse.class);
|
||||
|
||||
if (response.getStatus() < 200 || response.getStatus() >= 300) {
|
||||
throw new WebApplicationException(clientResponseToResponse(response));
|
||||
}
|
||||
|
||||
return Optional.of(response.getEntity(PreKeyResponseV1.class));
|
||||
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
return Optional.of(response);
|
||||
} catch (ProcessingException e) {
|
||||
logger.warn("PreKey", e);
|
||||
return Optional.absent();
|
||||
}
|
||||
@@ -134,34 +127,29 @@ public class FederatedClient {
|
||||
|
||||
public Optional<PreKeyResponseV2> getKeysV2(String destination, String device) {
|
||||
try {
|
||||
WebResource resource = client.resource(peer.getUrl()).path(String.format(PREKEY_PATH_DEVICE_V2, destination, device));
|
||||
PreKeyResponseV2 response = client.target(peer.getUrl())
|
||||
.path(String.format(PREKEY_PATH_DEVICE_V2, destination, device))
|
||||
.request()
|
||||
.accept(MediaType.APPLICATION_JSON_TYPE)
|
||||
.get(PreKeyResponseV2.class);
|
||||
|
||||
ClientResponse response = resource.accept(MediaType.APPLICATION_JSON)
|
||||
.header("Authorization", authorizationHeader)
|
||||
.get(ClientResponse.class);
|
||||
|
||||
if (response.getStatus() < 200 || response.getStatus() >= 300) {
|
||||
throw new WebApplicationException(clientResponseToResponse(response));
|
||||
}
|
||||
|
||||
return Optional.of(response.getEntity(PreKeyResponseV2.class));
|
||||
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
return Optional.of(response);
|
||||
} catch (ProcessingException e) {
|
||||
logger.warn("PreKey", e);
|
||||
return Optional.absent();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public int getUserCount() {
|
||||
try {
|
||||
WebResource resource = client.resource(peer.getUrl()).path(USER_COUNT_PATH);
|
||||
AccountCount count = resource.accept(MediaType.APPLICATION_JSON)
|
||||
.header("Authorization", authorizationHeader)
|
||||
.get(AccountCount.class);
|
||||
AccountCount count = client.target(peer.getUrl())
|
||||
.path(USER_COUNT_PATH)
|
||||
.request()
|
||||
.accept(MediaType.APPLICATION_JSON_TYPE)
|
||||
.get(AccountCount.class);
|
||||
|
||||
return count.getCount();
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
} catch (ProcessingException e) {
|
||||
logger.warn("User Count", e);
|
||||
return 0;
|
||||
}
|
||||
@@ -169,13 +157,14 @@ public class FederatedClient {
|
||||
|
||||
public List<ClientContact> getUserTokens(int offset) {
|
||||
try {
|
||||
WebResource resource = client.resource(peer.getUrl()).path(String.format(USER_TOKENS_PATH, offset));
|
||||
ClientContacts contacts = resource.accept(MediaType.APPLICATION_JSON)
|
||||
.header("Authorization", authorizationHeader)
|
||||
.get(ClientContacts.class);
|
||||
ClientContacts contacts = client.target(peer.getUrl())
|
||||
.path(String.format(USER_TOKENS_PATH, offset))
|
||||
.request()
|
||||
.accept(MediaType.APPLICATION_JSON_TYPE)
|
||||
.get(ClientContacts.class);
|
||||
|
||||
return contacts.getContacts();
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
} catch (ProcessingException e) {
|
||||
logger.warn("User Tokens", e);
|
||||
return null;
|
||||
}
|
||||
@@ -185,16 +174,16 @@ public class FederatedClient {
|
||||
throws IOException
|
||||
{
|
||||
try {
|
||||
WebResource resource = client.resource(peer.getUrl()).path(String.format(RELAY_MESSAGE_PATH, source, sourceDeviceId, destination));
|
||||
ClientResponse response = resource.type(MediaType.APPLICATION_JSON)
|
||||
.header("Authorization", authorizationHeader)
|
||||
.entity(messages)
|
||||
.put(ClientResponse.class);
|
||||
Response response = client.target(peer.getUrl())
|
||||
.path(String.format(RELAY_MESSAGE_PATH, source, sourceDeviceId, destination))
|
||||
.request()
|
||||
.put(Entity.entity(messages, MediaType.APPLICATION_JSON_TYPE));
|
||||
|
||||
if (response.getStatus() != 200 && response.getStatus() != 204) {
|
||||
throw new WebApplicationException(clientResponseToResponse(response));
|
||||
throw new WebApplicationException(response);
|
||||
}
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
|
||||
} catch (ProcessingException e) {
|
||||
logger.warn("sendMessage", e);
|
||||
throw new IOException(e);
|
||||
}
|
||||
@@ -204,26 +193,22 @@ public class FederatedClient {
|
||||
throws IOException
|
||||
{
|
||||
try {
|
||||
String path = String.format(RECEIPT_PATH, source, sourceDeviceId, destination, messageId);
|
||||
WebResource resource = client.resource(peer.getUrl()).path(path);
|
||||
ClientResponse response = resource.type(MediaType.APPLICATION_JSON)
|
||||
.header("Authorization", authorizationHeader)
|
||||
.put(ClientResponse.class);
|
||||
Response response = client.target(peer.getUrl())
|
||||
.path(String.format(RECEIPT_PATH, source, sourceDeviceId, destination, messageId))
|
||||
.request()
|
||||
.put(Entity.json(null));
|
||||
|
||||
if (response.getStatus() != 200 && response.getStatus() != 204) {
|
||||
throw new WebApplicationException(clientResponseToResponse(response));
|
||||
throw new WebApplicationException(response);
|
||||
}
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
} catch (ProcessingException e) {
|
||||
logger.warn("sendMessage", e);
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String getAuthorizationHeader(String federationName, FederatedPeer peer) {
|
||||
return "Basic " + Base64.encodeBytes((federationName + ":" + peer.getAuthenticationToken()).getBytes());
|
||||
}
|
||||
|
||||
private ClientConfig getClientConfig(FederatedPeer peer)
|
||||
private Client createClient(Environment environment, JerseyClientConfiguration configuration,
|
||||
String federationName, FederatedPeer peer)
|
||||
throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException, CertificateException
|
||||
{
|
||||
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
|
||||
@@ -232,13 +217,16 @@ public class FederatedClient {
|
||||
SSLContext sslContext = SSLContext.getInstance("TLS");
|
||||
sslContext.init(null, trustManagerFactory.getTrustManagers(), SecureRandom.getInstance("SHA1PRNG"));
|
||||
|
||||
ClientConfig config = new DefaultClientConfig();
|
||||
config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES,
|
||||
new HTTPSProperties(new StrictHostnameVerifier(), sslContext));
|
||||
config.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE);
|
||||
config.getSingletons().add(new JacksonJsonProvider().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false));
|
||||
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslContext, new DefaultHostnameVerifier());
|
||||
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register("https", sslConnectionSocketFactory).build();
|
||||
|
||||
return config;
|
||||
Client client = new JerseyClientBuilder(environment).using(configuration)
|
||||
.using(registry)
|
||||
.build("FederatedClient");
|
||||
|
||||
client.register(HttpAuthenticationFeature.basic(federationName, peer.getAuthenticationToken()));
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
private KeyStore initializeTrustStore(String name, String pemCertificate)
|
||||
@@ -264,19 +252,6 @@ public class FederatedClient {
|
||||
}
|
||||
}
|
||||
|
||||
private Response clientResponseToResponse(ClientResponse r) {
|
||||
Response.ResponseBuilder rb = Response.status(r.getStatus());
|
||||
|
||||
for (Map.Entry<String, List<String>> entry : r.getHeaders().entrySet()) {
|
||||
for (String value : entry.getValue()) {
|
||||
rb.header(entry.getKey(), value);
|
||||
}
|
||||
}
|
||||
|
||||
rb.entity(r.getEntityInputStream());
|
||||
return rb.build();
|
||||
}
|
||||
|
||||
public String getPeerName() {
|
||||
return peer.getName();
|
||||
}
|
||||
|
||||
@@ -25,13 +25,18 @@ import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import io.dropwizard.client.JerseyClientConfiguration;
|
||||
import io.dropwizard.setup.Environment;
|
||||
|
||||
public class FederatedClientManager {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(FederatedClientManager.class);
|
||||
|
||||
private final HashMap<String, FederatedClient> clients = new HashMap<>();
|
||||
|
||||
public FederatedClientManager(FederationConfiguration federationConfig)
|
||||
public FederatedClientManager(Environment environment,
|
||||
JerseyClientConfiguration clientConfig,
|
||||
FederationConfiguration federationConfig)
|
||||
throws IOException
|
||||
{
|
||||
List<FederatedPeer> peers = federationConfig.getPeers();
|
||||
@@ -40,7 +45,7 @@ public class FederatedClientManager {
|
||||
if (peers != null) {
|
||||
for (FederatedPeer peer : peers) {
|
||||
logger.info("Adding peer: " + peer.getName());
|
||||
clients.put(peer.getName(), new FederatedClient(identity, peer));
|
||||
clients.put(peer.getName(), new FederatedClient(environment, clientConfig, identity, peer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,9 +7,9 @@ import java.sql.SQLException;
|
||||
|
||||
import io.dropwizard.Configuration;
|
||||
import io.dropwizard.cli.ConfiguredCommand;
|
||||
import io.dropwizard.db.DataSourceFactory;
|
||||
import io.dropwizard.db.DatabaseConfiguration;
|
||||
import io.dropwizard.db.ManagedDataSource;
|
||||
import io.dropwizard.db.PooledDataSourceFactory;
|
||||
import io.dropwizard.setup.Bootstrap;
|
||||
import liquibase.Liquibase;
|
||||
import liquibase.exception.LiquibaseException;
|
||||
@@ -40,10 +40,8 @@ public abstract class AbstractLiquibaseCommand<T extends Configuration> extends
|
||||
@Override
|
||||
@SuppressWarnings("UseOfSystemOutOrSystemErr")
|
||||
protected void run(Bootstrap<T> bootstrap, Namespace namespace, T configuration) throws Exception {
|
||||
final DataSourceFactory dbConfig = strategy.getDataSourceFactory(configuration);
|
||||
dbConfig.setMaxSize(1);
|
||||
dbConfig.setMinSize(1);
|
||||
dbConfig.setInitialSize(1);
|
||||
final PooledDataSourceFactory dbConfig = strategy.getDataSourceFactory(configuration);
|
||||
dbConfig.asSingleConnectionPool();
|
||||
|
||||
try (final CloseableLiquibase liquibase = openLiquibase(dbConfig, namespace)) {
|
||||
run(namespace, liquibase);
|
||||
@@ -53,7 +51,7 @@ public abstract class AbstractLiquibaseCommand<T extends Configuration> extends
|
||||
}
|
||||
}
|
||||
|
||||
private CloseableLiquibase openLiquibase(final DataSourceFactory dataSourceFactory, final Namespace namespace)
|
||||
private CloseableLiquibase openLiquibase(final PooledDataSourceFactory dataSourceFactory, final Namespace namespace)
|
||||
throws ClassNotFoundException, SQLException, LiquibaseException
|
||||
{
|
||||
final ManagedDataSource dataSource = dataSourceFactory.build(new MetricRegistry(), "liquibase");
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.whispersystems.textsecuregcm.providers;
|
||||
|
||||
import javax.ws.rs.ext.ContextResolver;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
@Provider
|
||||
public class JacksonConfigurator implements ContextResolver<ObjectMapper> {
|
||||
|
||||
private final ObjectMapper mapper;
|
||||
|
||||
public JacksonConfigurator() {
|
||||
mapper = new ObjectMapper();
|
||||
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ObjectMapper getContext(Class<?> type) {
|
||||
return mapper;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
package org.whispersystems.textsecuregcm.push;
|
||||
|
||||
import com.sun.jersey.api.client.Client;
|
||||
import com.sun.jersey.api.client.ClientHandlerException;
|
||||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.sun.jersey.api.client.UniformInterfaceException;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.configuration.PushConfiguration;
|
||||
@@ -13,7 +9,11 @@ import org.whispersystems.textsecuregcm.entities.UnregisteredEvent;
|
||||
import org.whispersystems.textsecuregcm.entities.UnregisteredEventList;
|
||||
import org.whispersystems.textsecuregcm.util.Base64;
|
||||
|
||||
import javax.ws.rs.ProcessingException;
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
@@ -57,16 +57,17 @@ public class PushServiceClient {
|
||||
|
||||
private void sendPush(String path, Object entity) throws TransientPushFailureException {
|
||||
try {
|
||||
ClientResponse response = client.resource("http://" + host + ":" + port + path)
|
||||
.header("Authorization", authorization)
|
||||
.entity(entity, MediaType.APPLICATION_JSON)
|
||||
.put(ClientResponse.class);
|
||||
Response response = client.target("http://" + host + ":" + port)
|
||||
.path(path)
|
||||
.request()
|
||||
.header("Authorization", authorization)
|
||||
.put(Entity.entity(entity, MediaType.APPLICATION_JSON_TYPE));
|
||||
|
||||
if (response.getStatus() != 204 && response.getStatus() != 200) {
|
||||
logger.warn("PushServer response: " + response.getStatus() + " " + response.getStatusInfo().getReasonPhrase());
|
||||
throw new TransientPushFailureException("Bad response: " + response.getStatus());
|
||||
}
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
} catch (ProcessingException e) {
|
||||
logger.warn("Push error: ", e);
|
||||
throw new TransientPushFailureException(e);
|
||||
}
|
||||
@@ -74,12 +75,14 @@ public class PushServiceClient {
|
||||
|
||||
private List<UnregisteredEvent> getFeedback(String path) throws IOException {
|
||||
try {
|
||||
UnregisteredEventList unregisteredEvents = client.resource("http://" + host + ":" + port + path)
|
||||
UnregisteredEventList unregisteredEvents = client.target("http://" + host + ":" + port)
|
||||
.path(path)
|
||||
.request()
|
||||
.header("Authorization", authorization)
|
||||
.get(UnregisteredEventList.class);
|
||||
|
||||
return unregisteredEvents.getDevices();
|
||||
} catch (UniformInterfaceException | ClientHandlerException e) {
|
||||
} catch (ProcessingException e) {
|
||||
logger.warn("Request error:", e);
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public class AuthenticatedConnectListener implements WebSocketConnectListener {
|
||||
|
||||
@Override
|
||||
public void onWebSocketConnect(WebSocketSessionContext context) {
|
||||
final Account account = context.getAuthenticated(Account.class).get();
|
||||
final Account account = context.getAuthenticated(Account.class);
|
||||
final Device device = account.getAuthenticatedDevice().get();
|
||||
final long connectTime = System.currentTimeMillis();
|
||||
final WebsocketAddress address = new WebsocketAddress(account.getNumber(), device.getId());
|
||||
|
||||
@@ -8,6 +8,7 @@ import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.websocket.auth.AuthenticationException;
|
||||
import org.whispersystems.websocket.auth.WebSocketAuthenticator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import io.dropwizard.auth.basic.BasicCredentials;
|
||||
@@ -24,18 +25,18 @@ public class WebSocketAccountAuthenticator implements WebSocketAuthenticator<Acc
|
||||
@Override
|
||||
public Optional<Account> authenticate(UpgradeRequest request) throws AuthenticationException {
|
||||
try {
|
||||
Map<String, String[]> parameters = request.getParameterMap();
|
||||
String[] usernames = parameters.get("login");
|
||||
String[] passwords = parameters.get("password");
|
||||
Map<String, List<String>> parameters = request.getParameterMap();
|
||||
List<String> usernames = parameters.get("login");
|
||||
List<String> passwords = parameters.get("password");
|
||||
|
||||
if (usernames == null || usernames.length == 0 ||
|
||||
passwords == null || passwords.length == 0)
|
||||
if (usernames == null || usernames.size() == 0 ||
|
||||
passwords == null || passwords.size() == 0)
|
||||
{
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
BasicCredentials credentials = new BasicCredentials(usernames[0].replace(" ", "+"),
|
||||
passwords[0].replace(" ", "+"));
|
||||
BasicCredentials credentials = new BasicCredentials(usernames.get(0).replace(" ", "+"),
|
||||
passwords.get(0).replace(" ", "+"));
|
||||
|
||||
return accountAuthenticator.authenticate(credentials);
|
||||
} catch (io.dropwizard.auth.AuthenticationException e) {
|
||||
|
||||
@@ -27,31 +27,40 @@ import org.whispersystems.textsecuregcm.storage.Accounts;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.DirectoryManager;
|
||||
|
||||
import io.dropwizard.Application;
|
||||
import io.dropwizard.cli.ConfiguredCommand;
|
||||
import io.dropwizard.cli.EnvironmentCommand;
|
||||
import io.dropwizard.db.DataSourceFactory;
|
||||
import io.dropwizard.jdbi.ImmutableListContainerFactory;
|
||||
import io.dropwizard.jdbi.ImmutableSetContainerFactory;
|
||||
import io.dropwizard.jdbi.OptionalContainerFactory;
|
||||
import io.dropwizard.jdbi.args.OptionalArgumentFactory;
|
||||
import io.dropwizard.setup.Bootstrap;
|
||||
import io.dropwizard.setup.Environment;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
|
||||
public class DirectoryCommand extends ConfiguredCommand<WhisperServerConfiguration> {
|
||||
public class DirectoryCommand extends EnvironmentCommand<WhisperServerConfiguration> {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(DirectoryCommand.class);
|
||||
|
||||
public DirectoryCommand() {
|
||||
super("directory", "Update directory from DB and peers.");
|
||||
super(new Application<WhisperServerConfiguration>() {
|
||||
@Override
|
||||
public void run(WhisperServerConfiguration configuration, Environment environment)
|
||||
throws Exception
|
||||
{
|
||||
|
||||
}
|
||||
}, "directory", "Update directory from DB and peers.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run(Bootstrap<WhisperServerConfiguration> bootstrap,
|
||||
Namespace namespace,
|
||||
WhisperServerConfiguration config)
|
||||
protected void run(Environment environment, Namespace namespace,
|
||||
WhisperServerConfiguration configuration)
|
||||
throws Exception
|
||||
{
|
||||
try {
|
||||
DataSourceFactory dbConfig = config.getDataSourceFactory();
|
||||
DataSourceFactory dbConfig = configuration.getDataSourceFactory();
|
||||
DBI dbi = new DBI(dbConfig.getUrl(), dbConfig.getUser(), dbConfig.getPassword());
|
||||
|
||||
dbi.registerArgumentFactory(new OptionalArgumentFactory(dbConfig.getDriverClass()));
|
||||
@@ -60,11 +69,13 @@ public class DirectoryCommand extends ConfiguredCommand<WhisperServerConfigurati
|
||||
dbi.registerContainerFactory(new OptionalContainerFactory());
|
||||
|
||||
Accounts accounts = dbi.onDemand(Accounts.class);
|
||||
JedisPool cacheClient = new RedisClientFactory(config.getCacheConfiguration().getUrl()).getRedisClientPool();
|
||||
JedisPool redisClient = new RedisClientFactory(config.getDirectoryConfiguration().getUrl()).getRedisClientPool();
|
||||
JedisPool cacheClient = new RedisClientFactory(configuration.getCacheConfiguration().getUrl()).getRedisClientPool();
|
||||
JedisPool redisClient = new RedisClientFactory(configuration.getDirectoryConfiguration().getUrl()).getRedisClientPool();
|
||||
DirectoryManager directory = new DirectoryManager(redisClient);
|
||||
AccountsManager accountsManager = new AccountsManager(accounts, directory, cacheClient);
|
||||
FederatedClientManager federatedClientManager = new FederatedClientManager(config.getFederationConfiguration());
|
||||
FederatedClientManager federatedClientManager = new FederatedClientManager(environment,
|
||||
configuration.getJerseyClientConfiguration(),
|
||||
configuration.getFederationConfiguration());
|
||||
|
||||
DirectoryUpdater update = new DirectoryUpdater(accountsManager, federatedClientManager, directory);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user