Retire `AttachmentControllerV2`

This commit is contained in:
Jon Chambers 2024-10-21 09:58:25 -04:00 committed by Jon Chambers
parent 5abfef50fc
commit bda4788a34
11 changed files with 3 additions and 190 deletions

View File

@ -21,9 +21,6 @@ svr3.userIdTokenSharedSecret: dbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # bas
tus.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG=
awsAttachments.accessKey: test
awsAttachments.accessSecret: test
gcpAttachments.rsaSigningKey: |
-----BEGIN PRIVATE KEY-----
ABCDEFGHIJKLMNOPQRSTUVWXYZ/0123456789+abcdefghijklmnopqrstuvwxyz

View File

@ -239,13 +239,6 @@ messageCache: # Redis server configuration for message store cache
cluster:
configurationUri: redis://redis.example.com:6379/
awsAttachments: # AWS S3 configuration
bucket: aws-attachments
credentials:
accessKeyId: secret://awsAttachments.accessKey
secretAccessKey: secret://awsAttachments.accessSecret
region: us-west-2
gcpAttachments: # GCP Storage configuration
domain: example.com
email: user@example.cocm

View File

@ -17,7 +17,6 @@ import org.whispersystems.textsecuregcm.attachments.TusConfiguration;
import org.whispersystems.textsecuregcm.configuration.ApnConfiguration;
import org.whispersystems.textsecuregcm.configuration.AppleAppStoreConfiguration;
import org.whispersystems.textsecuregcm.configuration.ArtServiceConfiguration;
import org.whispersystems.textsecuregcm.configuration.AwsAttachmentsConfiguration;
import org.whispersystems.textsecuregcm.configuration.AwsCredentialsProviderFactory;
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
import org.whispersystems.textsecuregcm.configuration.BraintreeConfiguration;
@ -109,11 +108,6 @@ public class WhisperServerConfiguration extends Configuration {
@JsonProperty
private DynamoDbTables dynamoDbTables;
@NotNull
@Valid
@JsonProperty
private AwsAttachmentsConfiguration awsAttachments;
@NotNull
@Valid
@JsonProperty
@ -397,10 +391,6 @@ public class WhisperServerConfiguration extends Configuration {
return webSocket;
}
public AwsAttachmentsConfiguration getAwsAttachmentsConfiguration() {
return awsAttachments;
}
public GcpAttachmentsConfiguration getGcpAttachmentsConfiguration() {
return gcpAttachments;
}

View File

@ -110,7 +110,6 @@ import org.whispersystems.textsecuregcm.controllers.AccountController;
import org.whispersystems.textsecuregcm.controllers.AccountControllerV2;
import org.whispersystems.textsecuregcm.controllers.ArchiveController;
import org.whispersystems.textsecuregcm.controllers.ArtController;
import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV2;
import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV4;
import org.whispersystems.textsecuregcm.controllers.CallLinkController;
import org.whispersystems.textsecuregcm.controllers.CallRoutingController;
@ -1096,10 +1095,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
new AccountControllerV2(accountsManager, changeNumberManager, phoneVerificationTokenManager,
registrationLockVerificationManager, rateLimiters),
new ArtController(rateLimiters, artCredentialsGenerator),
new AttachmentControllerV2(rateLimiters,
config.getAwsAttachmentsConfiguration().credentials().accessKeyId().value(),
config.getAwsAttachmentsConfiguration().credentials().secretAccessKey().value(),
config.getAwsAttachmentsConfiguration().region(), config.getAwsAttachmentsConfiguration().bucket()),
new AttachmentControllerV4(rateLimiters, gcsAttachmentGenerator, tusAttachmentGenerator,
experimentEnrollmentManager),
new ArchiveController(backupAuthManager, backupManager),

View File

@ -1,14 +0,0 @@
/*
* Copyright 2013 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.configuration;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
public record AwsAttachmentsConfiguration(@NotNull @Valid StaticAwsCredentialsFactory credentials,
@NotBlank String bucket,
@NotBlank String region) {
}

View File

@ -1,84 +0,0 @@
/*
* Copyright 2013 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.controllers;
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
import com.google.common.net.HttpHeaders;
import io.dropwizard.auth.Auth;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tags;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.security.SecureRandom;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.whispersystems.textsecuregcm.auth.AuthenticatedDevice;
import org.whispersystems.textsecuregcm.entities.AttachmentDescriptorV2;
import org.whispersystems.textsecuregcm.limits.RateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.metrics.UserAgentTagUtil;
import org.whispersystems.textsecuregcm.s3.PolicySigner;
import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator;
import org.whispersystems.textsecuregcm.util.Conversions;
import org.whispersystems.textsecuregcm.util.Pair;
import org.whispersystems.websocket.auth.ReadOnly;
// To be removed when Android 7.12 reaches saturation, likely some time toward the end of October 2024
@Deprecated(forRemoval = true)
@Path("/v2/attachments")
@Tag(name = "Attachments")
public class AttachmentControllerV2 {
private final PostPolicyGenerator policyGenerator;
private final PolicySigner policySigner;
private final RateLimiter rateLimiter;
private static final String CREATE_UPLOAD_COUNTER_NAME = name(AttachmentControllerV2.class, "uploadForm");
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);
}
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/form/upload")
public AttachmentDescriptorV2 getAttachmentUploadForm(
@ReadOnly @Auth AuthenticatedDevice auth,
@HeaderParam(HttpHeaders.USER_AGENT) String userAgent)
throws RateLimitExceededException {
rateLimiter.validate(auth.getAccount().getUuid());
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
long attachmentId = generateAttachmentId();
String objectName = String.valueOf(attachmentId);
Pair<String, String> policy = policyGenerator.createFor(now, objectName, 100 * 1024 * 1024);
String signature = policySigner.getSignature(now, policy.second());
Metrics.counter(CREATE_UPLOAD_COUNTER_NAME, Tags.of(UserAgentTagUtil.getPlatformTag(userAgent))).increment();
return new AttachmentDescriptorV2(attachmentId, objectName, policy.first(),
"private", "AWS4-HMAC-SHA256",
now.format(PostPolicyGenerator.AWS_DATE_TIME),
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);
}
}

View File

@ -30,7 +30,8 @@ import org.whispersystems.websocket.auth.ReadOnly;
/**
* The V4 API is identical to the {@link AttachmentControllerV3} API, but supports an additional TUS based cdn type (cdn3)
* The attachment controller generates "upload forms" for authenticated users that permit them to upload files
* (message attachments) to a remote storage location. The location may be selected by the server at runtime.
*/
@Path("/v4/attachments")
@Tag(name = "Attachments")

View File

@ -1,23 +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 record AttachmentDescriptorV2(long attachmentId,
String key,
String credential,
String acl,
String algorithm,
String date,
String policy,
String signature) {
@JsonProperty
public String attachmentIdString() {
return String.valueOf(attachmentId);
}
}

View File

@ -36,7 +36,6 @@ import org.whispersystems.textsecuregcm.attachments.TusAttachmentGenerator;
import org.whispersystems.textsecuregcm.attachments.TusConfiguration;
import org.whispersystems.textsecuregcm.auth.AuthenticatedDevice;
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
import org.whispersystems.textsecuregcm.entities.AttachmentDescriptorV2;
import org.whispersystems.textsecuregcm.entities.AttachmentDescriptorV3;
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
import org.whispersystems.textsecuregcm.limits.RateLimiter;
@ -47,7 +46,7 @@ import org.whispersystems.textsecuregcm.util.SystemMapper;
import org.whispersystems.textsecuregcm.util.TestRandomUtil;
@ExtendWith(DropwizardExtensionsSupport.class)
class AttachmentControllerTest {
class AttachmentControllerV4Test {
private static final RateLimiter RATE_LIMITER = mock(RateLimiter.class);
@ -93,8 +92,6 @@ class AttachmentControllerTest {
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedDevice.class))
.setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new AttachmentControllerV2(RATE_LIMITERS, "accessKey", "accessSecret", "us-east-1",
"attachmentv2-bucket"))
.addProvider(new AttachmentControllerV4(RATE_LIMITERS,
gcsAttachmentGenerator,
new TusAttachmentGenerator(new TusConfiguration(new SecretBytes(TUS_SECRET), TUS_URL)),
@ -179,33 +176,4 @@ class AttachmentControllerTest {
assertThat(credentialParts[3]).isEqualTo("storage");
assertThat(credentialParts[4]).isEqualTo("goog4_request");
}
@Test
void testV2Form() throws IOException {
AttachmentDescriptorV2 descriptor = resources.getJerseyTest()
.target("/v2/attachments/form/upload")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.get(AttachmentDescriptorV2.class);
assertThat(descriptor.key()).isEqualTo(descriptor.attachmentIdString());
assertThat(descriptor.acl()).isEqualTo("private");
assertThat(descriptor.algorithm()).isEqualTo("AWS4-HMAC-SHA256");
assertThat(descriptor.attachmentId()).isGreaterThan(0);
assertThat(String.valueOf(descriptor.attachmentId())).isEqualTo(descriptor.attachmentIdString());
String[] credentialParts = descriptor.credential().split("/");
assertThat(credentialParts[0]).isEqualTo("accessKey");
assertThat(credentialParts[2]).isEqualTo("us-east-1");
assertThat(credentialParts[3]).isEqualTo("s3");
assertThat(credentialParts[4]).isEqualTo("aws4_request");
assertThat(descriptor.date()).isNotBlank();
assertThat(descriptor.policy()).isNotBlank();
assertThat(descriptor.signature()).isNotBlank();
assertThat(new String(Base64.getDecoder().decode(descriptor.policy()))).contains("[\"content-length-range\", 1, 104857600]");
}
}

View File

@ -58,9 +58,6 @@ svr3.userIdTokenSharedSecret: dbcdefghijklmnopqrstuvwxyz0123456789ABCDEFG= # bas
tus.userAuthenticationTokenSharedSecret: abcdefghijklmnopqrstuvwxyz0123456789ABCDEFG=
awsAttachments.accessKey: test
awsAttachments.accessSecret: test
# The below private key was key generated exclusively for testing purposes. Do not use it in any other context.
gcpAttachments.rsaSigningKey: |
-----BEGIN PRIVATE KEY-----

View File

@ -235,13 +235,6 @@ messageCache: # Redis server configuration for message store cache
cluster:
type: local
awsAttachments: # AWS S3 configuration
bucket: aws-attachments
credentials:
accessKeyId: secret://awsAttachments.accessKey
secretAccessKey: secret://awsAttachments.accessSecret
region: us-west-2
gcpAttachments: # GCP Storage configuration
domain: example.com
email: user@example.cocm