diff --git a/service/pom.xml b/service/pom.xml index 9b28b4f9e..c9d7eb1be 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -289,10 +289,6 @@ com.amazonaws aws-java-sdk-core - - com.amazonaws - aws-java-sdk-s3 - com.amazonaws dynamodb-lock-client diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index 000c32f7f..f7c671ada 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -80,7 +80,6 @@ import org.whispersystems.textsecuregcm.badges.ResourceBundleLevelTranslator; import org.whispersystems.textsecuregcm.configuration.DirectoryServerConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.controllers.AccountController; -import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV1; import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV2; import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV3; import org.whispersystems.textsecuregcm.controllers.CertificateController; @@ -136,7 +135,6 @@ import org.whispersystems.textsecuregcm.metrics.MicrometerRegistryManager; import org.whispersystems.textsecuregcm.metrics.NetworkReceivedGauge; import org.whispersystems.textsecuregcm.metrics.NetworkSentGauge; import org.whispersystems.textsecuregcm.metrics.OperatingSystemMemoryGauge; -import org.whispersystems.textsecuregcm.push.PushLatencyManager; import org.whispersystems.textsecuregcm.metrics.ReportedMessageMetricsListener; import org.whispersystems.textsecuregcm.metrics.TrafficSource; import org.whispersystems.textsecuregcm.providers.MultiRecipientMessageProvider; @@ -148,6 +146,7 @@ import org.whispersystems.textsecuregcm.push.ClientPresenceManager; import org.whispersystems.textsecuregcm.push.FcmSender; import org.whispersystems.textsecuregcm.push.MessageSender; import org.whispersystems.textsecuregcm.push.ProvisioningManager; +import org.whispersystems.textsecuregcm.push.PushLatencyManager; import org.whispersystems.textsecuregcm.push.PushNotificationManager; import org.whispersystems.textsecuregcm.push.ReceiptSender; import org.whispersystems.textsecuregcm.recaptcha.RecaptchaClient; @@ -632,7 +631,6 @@ public class WhisperServerService extends Application commonControllers = Lists.newArrayList( - new AttachmentControllerV1(rateLimiters, config.getAwsAttachmentsConfiguration().getAccessKey(), config.getAwsAttachmentsConfiguration().getAccessSecret(), config.getAwsAttachmentsConfiguration().getBucket()), new AttachmentControllerV2(rateLimiters, config.getAwsAttachmentsConfiguration().getAccessKey(), config.getAwsAttachmentsConfiguration().getAccessSecret(), config.getAwsAttachmentsConfiguration().getRegion(), config.getAwsAttachmentsConfiguration().getBucket()), new AttachmentControllerV3(rateLimiters, config.getGcpAttachmentsConfiguration().getDomain(), config.getGcpAttachmentsConfiguration().getEmail(), config.getGcpAttachmentsConfiguration().getMaxSizeInBytes(), config.getGcpAttachmentsConfiguration().getPathPrefix(), config.getGcpAttachmentsConfiguration().getRsaSigningKey()), new CertificateController(new CertificateGenerator(config.getDeliveryCertificate().getCertificate(), config.getDeliveryCertificate().getPrivateKey(), config.getDeliveryCertificate().getExpiresDays()), zkAuthOperations, clock), diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerBase.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerBase.java deleted file mode 100644 index 81286e624..000000000 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerBase.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2013-2020 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.textsecuregcm.controllers; - -import org.whispersystems.textsecuregcm.util.Conversions; - -import java.security.SecureRandom; - -public class AttachmentControllerBase { - - protected long generateAttachmentId() { - byte[] attachmentBytes = new byte[8]; - new SecureRandom().nextBytes(attachmentBytes); - - attachmentBytes[0] = (byte)(attachmentBytes[0] & 0x7F); - return Conversions.byteArrayToLong(attachmentBytes); - } - -} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV1.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV1.java deleted file mode 100644 index 4ffc65cd0..000000000 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV1.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2013-2021 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ -package org.whispersystems.textsecuregcm.controllers; - -import com.amazonaws.HttpMethod; -import com.codahale.metrics.annotation.Timed; -import io.dropwizard.auth.Auth; -import java.net.URL; -import java.util.stream.Stream; -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 org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; -import org.whispersystems.textsecuregcm.entities.AttachmentDescriptorV1; -import org.whispersystems.textsecuregcm.entities.AttachmentUri; -import org.whispersystems.textsecuregcm.limits.RateLimiters; -import org.whispersystems.textsecuregcm.s3.UrlSigner; - - -@Path("/v1/attachments") -public class AttachmentControllerV1 extends AttachmentControllerBase { - - @SuppressWarnings("unused") - private final Logger logger = LoggerFactory.getLogger(AttachmentControllerV1.class); - - private static final String[] UNACCELERATED_REGIONS = {"+20", "+971", "+968", "+974"}; - - private final RateLimiters rateLimiters; - private final UrlSigner urlSigner; - - public AttachmentControllerV1(RateLimiters rateLimiters, String accessKey, String accessSecret, String bucket) { - this.rateLimiters = rateLimiters; - this.urlSigner = new UrlSigner(accessKey, accessSecret, bucket); - } - - @Timed - @GET - @Produces(MediaType.APPLICATION_JSON) - public AttachmentDescriptorV1 allocateAttachment(@Auth AuthenticatedAccount auth) throws RateLimitExceededException { - - rateLimiters.getAttachmentLimiter().validate(auth.getAccount().getUuid()); - - long attachmentId = generateAttachmentId(); - URL url = urlSigner.getPreSignedUrl(attachmentId, HttpMethod.PUT, - Stream.of(UNACCELERATED_REGIONS).anyMatch(region -> auth.getAccount().getNumber().startsWith(region))); - - return new AttachmentDescriptorV1(attachmentId, url.toExternalForm()); - } - - @Timed - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/{attachmentId}") - public AttachmentUri redirectToAttachment(@Auth AuthenticatedAccount auth, - @PathParam("attachmentId") long attachmentId) { - return new AttachmentUri(urlSigner.getPreSignedUrl(attachmentId, HttpMethod.GET, - Stream.of(UNACCELERATED_REGIONS).anyMatch(region -> auth.getAccount().getNumber().startsWith(region)))); - } - -} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV2.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV2.java index 09dd37be9..b0d5021af 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV2.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV2.java @@ -7,6 +7,7 @@ package org.whispersystems.textsecuregcm.controllers; import com.codahale.metrics.annotation.Timed; import io.dropwizard.auth.Auth; +import java.security.SecureRandom; import java.time.ZoneOffset; import java.time.ZonedDateTime; import javax.ws.rs.GET; @@ -19,19 +20,21 @@ import org.whispersystems.textsecuregcm.limits.RateLimiter; import org.whispersystems.textsecuregcm.limits.RateLimiters; import org.whispersystems.textsecuregcm.s3.PolicySigner; import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator; +import org.whispersystems.textsecuregcm.util.Conversions; import org.whispersystems.textsecuregcm.util.Pair; @Path("/v2/attachments") -public class AttachmentControllerV2 extends AttachmentControllerBase { +public class AttachmentControllerV2 { private final PostPolicyGenerator policyGenerator; - private final PolicySigner policySigner; - private final RateLimiter rateLimiter; + private final PolicySigner policySigner; + private final RateLimiter rateLimiter; - public AttachmentControllerV2(RateLimiters rateLimiters, String accessKey, String accessSecret, String region, String bucket) { - this.rateLimiter = rateLimiters.getAttachmentLimiter(); - this.policyGenerator = new PostPolicyGenerator(region, bucket, accessKey); - this.policySigner = new PolicySigner(accessSecret, region); + public AttachmentControllerV2(RateLimiters rateLimiters, String accessKey, String accessSecret, String region, + String bucket) { + this.rateLimiter = rateLimiters.getAttachmentLimiter(); + this.policyGenerator = new PostPolicyGenerator(region, bucket, accessKey); + this.policySigner = new PolicySigner(accessSecret, region); } @Timed @@ -54,5 +57,12 @@ public class AttachmentControllerV2 extends AttachmentControllerBase { policy.second(), signature); } + private long generateAttachmentId() { + byte[] attachmentBytes = new byte[8]; + new SecureRandom().nextBytes(attachmentBytes); + + attachmentBytes[0] = (byte) (attachmentBytes[0] & 0x7F); + return Conversions.byteArrayToLong(attachmentBytes); + } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV3.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV3.java index 631730420..cb54b22dc 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV3.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AttachmentControllerV3.java @@ -29,7 +29,7 @@ import org.whispersystems.textsecuregcm.limits.RateLimiter; import org.whispersystems.textsecuregcm.limits.RateLimiters; @Path("/v3/attachments") -public class AttachmentControllerV3 extends AttachmentControllerBase { +public class AttachmentControllerV3 { @Nonnull private final RateLimiter rateLimiter; diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/AttachmentDescriptorV1.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/AttachmentDescriptorV1.java deleted file mode 100644 index b014e2071..000000000 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/AttachmentDescriptorV1.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2013-2020 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ -package org.whispersystems.textsecuregcm.entities; - -import com.fasterxml.jackson.annotation.JsonProperty; - -public class AttachmentDescriptorV1 { - - @JsonProperty - private long id; - - @JsonProperty - private String idString; - - @JsonProperty - private String location; - - public AttachmentDescriptorV1(long id, String location) { - this.id = id; - this.idString = String.valueOf(id); - this.location = location; - } - - public AttachmentDescriptorV1() {} - - public long getId() { - return id; - } - - public String getLocation() { - return location; - } - - public String getIdString() { - return idString; - } -} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/s3/UrlSigner.java b/service/src/main/java/org/whispersystems/textsecuregcm/s3/UrlSigner.java deleted file mode 100644 index 34c6fd1b5..000000000 --- a/service/src/main/java/org/whispersystems/textsecuregcm/s3/UrlSigner.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2013-2020 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ -package org.whispersystems.textsecuregcm.s3; - -import com.amazonaws.HttpMethod; -import com.amazonaws.auth.AWSCredentials; -import com.amazonaws.auth.BasicAWSCredentials; -import com.amazonaws.services.s3.AmazonS3; -import com.amazonaws.services.s3.AmazonS3Client; -import com.amazonaws.services.s3.S3ClientOptions; -import com.amazonaws.services.s3.model.GeneratePresignedUrlRequest; - -import java.net.URL; -import java.util.Date; - -public class UrlSigner { - - private static final long DURATION = 60 * 60 * 1000; - - private final AWSCredentials credentials; - private final String bucket; - - public UrlSigner(String accessKey, String accessSecret, String bucket) { - this.credentials = new BasicAWSCredentials(accessKey, accessSecret); - this.bucket = bucket; - } - - public URL getPreSignedUrl(long attachmentId, HttpMethod method, boolean unaccelerated) { - AmazonS3 client = new AmazonS3Client(credentials); - GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucket, String.valueOf(attachmentId), method); - - request.setExpiration(new Date(System.currentTimeMillis() + DURATION)); - request.setContentType("application/octet-stream"); - - if (unaccelerated) { - client.setS3ClientOptions(S3ClientOptions.builder().setPathStyleAccess(true).build()); - } else { - client.setS3ClientOptions(S3ClientOptions.builder().setAccelerateModeEnabled(true).build()); - } - - return client.generatePresignedUrl(request); - } - -} diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AttachmentControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AttachmentControllerTest.java index c52740eb8..5e7ecb85c 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AttachmentControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/AttachmentControllerTest.java @@ -35,13 +35,10 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount; -import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV1; import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV2; import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV3; -import org.whispersystems.textsecuregcm.entities.AttachmentDescriptorV1; import org.whispersystems.textsecuregcm.entities.AttachmentDescriptorV2; import org.whispersystems.textsecuregcm.entities.AttachmentDescriptorV3; -import org.whispersystems.textsecuregcm.entities.AttachmentUri; import org.whispersystems.textsecuregcm.limits.RateLimiter; import org.whispersystems.textsecuregcm.limits.RateLimiters; import org.whispersystems.textsecuregcm.tests.util.AuthHelper; @@ -83,7 +80,6 @@ class AttachmentControllerTest { ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class))) .setMapper(SystemMapper.getMapper()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory()) - .addResource(new AttachmentControllerV1(rateLimiters, "accessKey", "accessSecret", "attachment-bucket")) .addResource(new AttachmentControllerV2(rateLimiters, "accessKey", "accessSecret", "us-east-1", "attachmentv2-bucket")) .addResource(new AttachmentControllerV3(rateLimiters, "some-cdn.signal.org", "signal@example.com", 1000, "/attach-here", RSA_PRIVATE_KEY_PEM)) .build(); @@ -198,53 +194,4 @@ class AttachmentControllerTest { assertThat(response.getStatus()).isEqualTo(401); } - - @Test - void testAcceleratedPut() { - AttachmentDescriptorV1 descriptor = resources.getJerseyTest() - .target("/v1/attachments/") - .request() - .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .get(AttachmentDescriptorV1.class); - - assertThat(descriptor.getLocation()).startsWith("https://attachment-bucket.s3-accelerate.amazonaws.com"); - assertThat(descriptor.getId()).isGreaterThan(0); - assertThat(descriptor.getIdString()).isNotBlank(); - } - - @Test - void testUnacceleratedPut() { - AttachmentDescriptorV1 descriptor = resources.getJerseyTest() - .target("/v1/attachments/") - .request() - .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) - .get(AttachmentDescriptorV1.class); - - assertThat(descriptor.getLocation()).startsWith("https://s3.amazonaws.com"); - assertThat(descriptor.getId()).isGreaterThan(0); - assertThat(descriptor.getIdString()).isNotBlank(); - } - - @Test - void testAcceleratedGet() throws MalformedURLException { - AttachmentUri uri = resources.getJerseyTest() - .target("/v1/attachments/1234") - .request() - .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .get(AttachmentUri.class); - - assertThat(uri.getLocation().getHost()).isEqualTo("attachment-bucket.s3-accelerate.amazonaws.com"); - } - - @Test - void testUnacceleratedGet() throws MalformedURLException { - AttachmentUri uri = resources.getJerseyTest() - .target("/v1/attachments/1234") - .request() - .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) - .get(AttachmentUri.class); - - assertThat(uri.getLocation().getHost()).isEqualTo("s3.amazonaws.com"); - } - } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/UrlSignerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/UrlSignerTest.java deleted file mode 100644 index 51e28b716..000000000 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/util/UrlSignerTest.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2013-2020 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.textsecuregcm.tests.util; - -import static org.assertj.core.api.Assertions.assertThat; - -import com.amazonaws.HttpMethod; -import java.net.URL; -import org.junit.jupiter.api.Test; -import org.whispersystems.textsecuregcm.s3.UrlSigner; - -class UrlSignerTest { - - @Test - void testTransferAcceleration() { - UrlSigner signer = new UrlSigner("foo", "bar", "attachments-test"); - URL url = signer.getPreSignedUrl(1234, HttpMethod.GET, false); - - assertThat(url).hasHost("attachments-test.s3-accelerate.amazonaws.com"); - } - - @Test - void testTransferUnaccelerated() { - UrlSigner signer = new UrlSigner("foo", "bar", "attachments-test"); - URL url = signer.getPreSignedUrl(1234, HttpMethod.GET, true); - - assertThat(url).hasHost("s3.amazonaws.com"); - } - -}