Remove Subscriptions.PCI attribute

This commit is contained in:
Chris Eager
2023-01-03 15:26:19 -06:00
committed by Chris Eager
parent 010f88a2ad
commit b02b00818b
4 changed files with 38 additions and 156 deletions

View File

@@ -416,7 +416,7 @@ class SubscriptionControllerTest {
final SubscriptionManager.Record recordWithCustomerId = SubscriptionManager.Record.from(record.user,
dynamoItemWithProcessorCustomer);
when(SUBSCRIPTION_MANAGER.updateProcessorAndCustomerId(any(SubscriptionManager.Record.class), any(),
when(SUBSCRIPTION_MANAGER.setProcessorAndCustomerId(any(SubscriptionManager.Record.class), any(),
any(Instant.class)))
.thenReturn(CompletableFuture.completedFuture(recordWithCustomerId));

View File

@@ -9,22 +9,18 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.whispersystems.textsecuregcm.storage.SubscriptionManager.GetResult.Type.FOUND;
import static org.whispersystems.textsecuregcm.storage.SubscriptionManager.GetResult.Type.NOT_STORED;
import static org.whispersystems.textsecuregcm.storage.SubscriptionManager.GetResult.Type.PASSWORD_MISMATCH;
import static org.whispersystems.textsecuregcm.util.AttributeValues.b;
import static org.whispersystems.textsecuregcm.util.AttributeValues.m;
import static org.whispersystems.textsecuregcm.util.AttributeValues.n;
import static org.whispersystems.textsecuregcm.util.AttributeValues.s;
import java.security.SecureRandom;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Base64;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import javax.annotation.Nonnull;
import javax.ws.rs.ClientErrorException;
import org.assertj.core.api.Condition;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -33,7 +29,6 @@ import org.whispersystems.textsecuregcm.storage.SubscriptionManager.Record;
import org.whispersystems.textsecuregcm.subscriptions.ProcessorCustomer;
import org.whispersystems.textsecuregcm.subscriptions.SubscriptionProcessor;
import software.amazon.awssdk.services.dynamodb.model.AttributeDefinition;
import software.amazon.awssdk.services.dynamodb.model.AttributeValue;
import software.amazon.awssdk.services.dynamodb.model.GlobalSecondaryIndex;
import software.amazon.awssdk.services.dynamodb.model.KeySchemaElement;
import software.amazon.awssdk.services.dynamodb.model.KeyType;
@@ -150,7 +145,7 @@ class SubscriptionManagerTest {
}
@Test
void testUpdateCustomerIdAndProcessor() throws Exception {
void testSetCustomerIdAndProcessor() throws Exception {
Instant subscriptionUpdated = Instant.ofEpochSecond(NOW_EPOCH_SECONDS + 1);
assertThat(subscriptionManager.create(user, password, created)).succeedsWithin(Duration.ofSeconds(3));
@@ -158,22 +153,34 @@ class SubscriptionManagerTest {
assertThat(getUser).succeedsWithin(Duration.ofSeconds(3));
final Record userRecord = getUser.get().record;
assertThat(subscriptionManager.updateProcessorAndCustomerId(userRecord,
assertThat(subscriptionManager.setProcessorAndCustomerId(userRecord,
new ProcessorCustomer(customer, SubscriptionProcessor.STRIPE),
subscriptionUpdated)).succeedsWithin(Duration.ofSeconds(3))
.hasFieldOrPropertyWithValue("processorCustomer",
Optional.of(new ProcessorCustomer(customer, SubscriptionProcessor.STRIPE)))
.hasFieldOrPropertyWithValue("processorsToCustomerIds", Map.of(SubscriptionProcessor.STRIPE, customer));
Optional.of(new ProcessorCustomer(customer, SubscriptionProcessor.STRIPE)));
final Condition<Throwable> clientError409Condition = new Condition<>(e ->
e instanceof ClientErrorException cee && cee.getResponse().getStatus() == 409, "Client error: 409");
// changing the customer ID is not permitted
assertThat(
subscriptionManager.updateProcessorAndCustomerId(userRecord,
subscriptionManager.setProcessorAndCustomerId(userRecord,
new ProcessorCustomer(customer + "1", SubscriptionProcessor.STRIPE),
subscriptionUpdated)).succeedsWithin(Duration.ofSeconds(3))
.hasFieldOrPropertyWithValue("processorCustomer",
Optional.of(new ProcessorCustomer(customer, SubscriptionProcessor.STRIPE)))
.hasFieldOrPropertyWithValue("processorsToCustomerIds", Map.of(SubscriptionProcessor.STRIPE, customer));
subscriptionUpdated)).failsWithin(Duration.ofSeconds(3))
.withThrowableOfType(ExecutionException.class)
.withCauseInstanceOf(ClientErrorException.class)
.extracting(Throwable::getCause)
.satisfies(clientError409Condition);
// TODO test new customer ID with new processor does change the customer ID, once there is another processor
// calling setProcessorAndCustomerId() with the same customer ID is also an error
assertThat(
subscriptionManager.setProcessorAndCustomerId(userRecord,
new ProcessorCustomer(customer, SubscriptionProcessor.STRIPE),
subscriptionUpdated)).failsWithin(Duration.ofSeconds(3))
.withThrowableOfType(ExecutionException.class)
.withCauseInstanceOf(ClientErrorException.class)
.extracting(Throwable::getCause)
.satisfies(clientError409Condition);
assertThat(subscriptionManager.getSubscriberUserByProcessorCustomer(
new ProcessorCustomer(customer, SubscriptionProcessor.STRIPE)))
@@ -190,7 +197,7 @@ class SubscriptionManagerTest {
assertThat(getUser).succeedsWithin(Duration.ofSeconds(3));
final Record userRecord = getUser.get().record;
assertThat(subscriptionManager.updateProcessorAndCustomerId(userRecord,
assertThat(subscriptionManager.setProcessorAndCustomerId(userRecord,
new ProcessorCustomer(customer, SubscriptionProcessor.STRIPE),
subscriptionUpdated)).succeedsWithin(Duration.ofSeconds(3));
assertThat(subscriptionManager.getSubscriberUserByProcessorCustomer(
@@ -253,107 +260,6 @@ class SubscriptionManagerTest {
});
}
@Test
void testSubscriptionAddProcessorAttribute() throws Exception {
final byte[] user = new byte[16];
Arrays.fill(user, (byte) 1);
final byte[] hmac = new byte[16];
Arrays.fill(hmac, (byte) 2);
final String customerId = "abcdef";
assertThat(subscriptionManager.create(user, hmac, Instant.now()))
.succeedsWithin(Duration.ofSeconds(1));
final CompletableFuture<GetResult> firstGetResult = subscriptionManager.get(user, hmac);
assertThat(firstGetResult).succeedsWithin(Duration.ofSeconds(1));
final Record firstRecord = firstGetResult.get().record;
assertThat(firstRecord.processorCustomer).isNull();
assertThat(firstRecord.processorsToCustomerIds).isEmpty();
subscriptionManager.updateProcessorAndCustomerId(firstRecord,
new ProcessorCustomer(customerId, SubscriptionProcessor.STRIPE), Instant.now())
.get(1, TimeUnit.SECONDS);
// Try to update the user to have a different customer ID. This should quietly fail,
// and just return the existing customer ID.
final CompletableFuture<Record> firstUpdate = subscriptionManager.updateProcessorAndCustomerId(firstRecord,
new ProcessorCustomer(customerId + "something else", SubscriptionProcessor.STRIPE),
Instant.now());
assertThat(firstUpdate).succeedsWithin(Duration.ofSeconds(1));
final String firstUpdateCustomerId = firstUpdate.get().getProcessorCustomer().orElseThrow().customerId();
assertThat(firstUpdateCustomerId).isEqualTo(customerId);
// Now update with the existing customer ID. All fields should now be populated.
final CompletableFuture<Record> secondUpdate = subscriptionManager.updateProcessorAndCustomerId(firstRecord,
new ProcessorCustomer(customerId, SubscriptionProcessor.STRIPE), Instant.now());
assertThat(secondUpdate).succeedsWithin(Duration.ofSeconds(1));
final String secondUpdateCustomerId = secondUpdate.get().getProcessorCustomer().orElseThrow().customerId();
assertThat(secondUpdateCustomerId).isEqualTo(customerId);
final CompletableFuture<GetResult> secondGetResult = subscriptionManager.get(user, hmac);
assertThat(secondGetResult).succeedsWithin(Duration.ofSeconds(1));
final Record secondRecord = secondGetResult.get().record;
assertThat(secondRecord.getProcessorCustomer())
.isPresent()
.get()
.isEqualTo(new ProcessorCustomer(customerId, SubscriptionProcessor.STRIPE));
assertThat(secondRecord.processorsToCustomerIds).isEqualTo(Map.of(SubscriptionProcessor.STRIPE, customerId));
}
@Test
void testUpdateEmptyProcessorCustomerWithValueInMap() throws Exception {
// it isnt possible to create this exact data setup in current code, but this tests the conditional update expression
final Map<String, AttributeValue> processorCustomers = Map.of(
SubscriptionProcessor.STRIPE.name(), s(customer)
);
final Map<String, AttributeValue> dynamoItem = Map.of(
SubscriptionManager.KEY_USER, b(user),
SubscriptionManager.KEY_PASSWORD, b(password),
SubscriptionManager.KEY_PROCESSOR_CUSTOMER_IDS_MAP, m(processorCustomers),
SubscriptionManager.KEY_CREATED_AT, n(created.getEpochSecond()),
SubscriptionManager.KEY_ACCESSED_AT, n(Instant.now().getEpochSecond())
);
dynamoDbExtension.getDynamoDbAsyncClient().putItem(builder ->
builder.tableName(dynamoDbExtension.getTableName())
.item(dynamoItem)
).get(1, TimeUnit.SECONDS);
final CompletableFuture<GetResult> firstGet = subscriptionManager.get(user, password);
assertThat(firstGet)
.succeedsWithin(Duration.ofSeconds(1))
.extracting(r -> r.record)
.satisfies(record -> {
assertThat(record.processorCustomer).isNull();
assertThat(record.processorsToCustomerIds).size().isEqualTo(1);
assertThat(record.processorsToCustomerIds).contains(Map.entry(SubscriptionProcessor.STRIPE, customer));
});
final CompletableFuture<Record> update = subscriptionManager.updateProcessorAndCustomerId(firstGet.get().record,
new ProcessorCustomer(customer, SubscriptionProcessor.STRIPE), Instant.now());
assertThat(update)
.succeedsWithin(Duration.ofSeconds(1))
.satisfies(record -> {
// processorCustomer should not have been updated
assertThat(record.processorCustomer).isNull();
assertThat(record.processorsToCustomerIds).size().isEqualTo(1);
assertThat(record.processorsToCustomerIds).contains(Map.entry(SubscriptionProcessor.STRIPE, customer));
});
}
@Test
void testProcessorAndCustomerId() {
final ProcessorCustomer processorCustomer =
@@ -376,7 +282,6 @@ class SubscriptionManagerTest {
assertThat(record.user).isEqualTo(user);
assertThat(record.password).isEqualTo(password);
assertThat(record.processorCustomer).isNull();
assertThat(record.processorsToCustomerIds).isEmpty();
assertThat(record.createdAt).isEqualTo(created);
assertThat(record.subscriptionId).isNull();
assertThat(record.subscriptionCreatedAt).isNull();