From bd69905f2eb19253c99c854b4df3a500222babfe Mon Sep 17 00:00:00 2001 From: Chris Eager Date: Wed, 12 Oct 2022 12:41:45 -0500 Subject: [PATCH] Remove obsolete donation endpoint --- service/config/sample.yml | 8 -- .../WhisperServerConfiguration.java | 10 -- .../textsecuregcm/WhisperServerService.java | 2 +- .../configuration/DonationConfiguration.java | 78 --------------- .../controllers/DonationController.java | 99 +------------------ .../ApplePayAuthorizationRequest.java | 42 -------- .../ApplePayAuthorizationResponse.java | 44 --------- .../controllers/DonationControllerTest.java | 85 +--------------- 8 files changed, 5 insertions(+), 363 deletions(-) delete mode 100644 service/src/main/java/org/whispersystems/textsecuregcm/configuration/DonationConfiguration.java delete mode 100644 service/src/main/java/org/whispersystems/textsecuregcm/entities/ApplePayAuthorizationRequest.java delete mode 100644 service/src/main/java/org/whispersystems/textsecuregcm/entities/ApplePayAuthorizationResponse.java diff --git a/service/config/sample.yml b/service/config/sample.yml index ab694809a..3dc976734 100644 --- a/service/config/sample.yml +++ b/service/config/sample.yml @@ -294,14 +294,6 @@ paymentsService: # list of symbols for supported currencies - MOB -donation: - uri: donation.example.com # value - supportedCurrencies: - - # 1st supported currency - - # 2nd supported currency - - # ... - - # Nth supported currency - badges: badges: - id: TEST diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerConfiguration.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerConfiguration.java index be8f0ad4a..b5f3a411c 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerConfiguration.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerConfiguration.java @@ -24,7 +24,6 @@ import org.whispersystems.textsecuregcm.configuration.CdnConfiguration; import org.whispersystems.textsecuregcm.configuration.DatadogConfiguration; import org.whispersystems.textsecuregcm.configuration.DirectoryConfiguration; import org.whispersystems.textsecuregcm.configuration.DirectoryV2Configuration; -import org.whispersystems.textsecuregcm.configuration.DonationConfiguration; import org.whispersystems.textsecuregcm.configuration.DynamoDbClientConfiguration; import org.whispersystems.textsecuregcm.configuration.DynamoDbTables; import org.whispersystems.textsecuregcm.configuration.FcmConfiguration; @@ -219,11 +218,6 @@ public class WhisperServerConfiguration extends Configuration { @JsonProperty private AppConfigConfiguration appConfig; - @Valid - @NotNull - @JsonProperty - private DonationConfiguration donation; - @Valid @NotNull @JsonProperty @@ -409,10 +403,6 @@ public class WhisperServerConfiguration extends Configuration { return appConfig; } - public DonationConfiguration getDonationConfiguration() { - return donation; - } - public BadgesConfiguration getBadges() { return badges; } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index 30a109750..659826475 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -659,7 +659,7 @@ public class WhisperServerService extends Application supportedCurrencies; - private CircuitBreakerConfiguration circuitBreaker = new CircuitBreakerConfiguration(); - private RetryConfiguration retry = new RetryConfiguration(); - - @JsonProperty - @NotEmpty - public String getUri() { - return uri; - } - - @VisibleForTesting - public void setUri(final String uri) { - this.uri = uri; - } - - @JsonProperty - public String getDescription() { - return description; - } - - @VisibleForTesting - public void setDescription(final String description) { - this.description = description; - } - - @JsonProperty - @NotEmpty - public Set getSupportedCurrencies() { - return supportedCurrencies; - } - - @VisibleForTesting - public void setSupportedCurrencies(final Set supportedCurrencies) { - this.supportedCurrencies = supportedCurrencies; - } - - @JsonProperty - @NotNull - @Valid - public CircuitBreakerConfiguration getCircuitBreaker() { - return circuitBreaker; - } - - @VisibleForTesting - public void setCircuitBreaker(final CircuitBreakerConfiguration circuitBreaker) { - this.circuitBreaker = circuitBreaker; - } - - @JsonProperty - @NotNull - @Valid - public RetryConfiguration getRetry() { - return retry; - } - - @VisibleForTesting - public void setRetry(final RetryConfiguration retry) { - this.retry = retry; - } -} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DonationController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DonationController.java index 779759bb4..c6d363ae7 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DonationController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/DonationController.java @@ -6,30 +6,13 @@ package org.whispersystems.textsecuregcm.controllers; import com.codahale.metrics.annotation.Timed; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import io.dropwizard.auth.Auth; -import io.dropwizard.util.Strings; -import java.net.URI; -import java.net.http.HttpClient.Redirect; -import java.net.http.HttpClient.Version; -import java.net.http.HttpRequest; -import java.net.http.HttpResponse; -import java.net.http.HttpResponse.BodyHandlers; -import java.nio.charset.StandardCharsets; import java.time.Clock; -import java.time.Duration; import java.time.Instant; -import java.util.Base64; -import java.util.HashMap; -import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; -import java.util.concurrent.Executor; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.ForkJoinPool.ManagedBlocker; import java.util.function.Function; @@ -52,18 +35,11 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration; -import org.whispersystems.textsecuregcm.configuration.DonationConfiguration; -import org.whispersystems.textsecuregcm.configuration.StripeConfiguration; -import org.whispersystems.textsecuregcm.entities.ApplePayAuthorizationRequest; -import org.whispersystems.textsecuregcm.entities.ApplePayAuthorizationResponse; import org.whispersystems.textsecuregcm.entities.RedeemReceiptRequest; -import org.whispersystems.textsecuregcm.http.FaultTolerantHttpClient; -import org.whispersystems.textsecuregcm.http.FormDataBodyPublisher; import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.AccountBadge; import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.RedeemedReceiptsManager; -import org.whispersystems.textsecuregcm.util.SystemMapper; @Path("/v1/donation") public class DonationController { @@ -80,11 +56,6 @@ public class DonationController { private final AccountsManager accountsManager; private final BadgesConfiguration badgesConfiguration; private final ReceiptCredentialPresentationFactory receiptCredentialPresentationFactory; - private final URI uri; - private final String apiKey; - private final String description; - private final Set supportedCurrencies; - private final FaultTolerantHttpClient httpClient; public DonationController( @Nonnull final Clock clock, @@ -92,30 +63,13 @@ public class DonationController { @Nonnull final RedeemedReceiptsManager redeemedReceiptsManager, @Nonnull final AccountsManager accountsManager, @Nonnull final BadgesConfiguration badgesConfiguration, - @Nonnull final ReceiptCredentialPresentationFactory receiptCredentialPresentationFactory, - @Nonnull final Executor httpClientExecutor, - @Nonnull final DonationConfiguration configuration, - @Nonnull final StripeConfiguration stripeConfiguration) { + @Nonnull final ReceiptCredentialPresentationFactory receiptCredentialPresentationFactory) { this.clock = Objects.requireNonNull(clock); this.serverZkReceiptOperations = Objects.requireNonNull(serverZkReceiptOperations); this.redeemedReceiptsManager = Objects.requireNonNull(redeemedReceiptsManager); this.accountsManager = Objects.requireNonNull(accountsManager); this.badgesConfiguration = Objects.requireNonNull(badgesConfiguration); this.receiptCredentialPresentationFactory = Objects.requireNonNull(receiptCredentialPresentationFactory); - this.uri = URI.create(configuration.getUri()); - this.apiKey = stripeConfiguration.getApiKey(); - this.description = configuration.getDescription(); - this.supportedCurrencies = configuration.getSupportedCurrencies(); - this.httpClient = FaultTolerantHttpClient.newBuilder() - .withCircuitBreaker(configuration.getCircuitBreaker()) - .withRetry(configuration.getRetry()) - .withVersion(Version.HTTP_2) - .withConnectTimeout(Duration.ofSeconds(10)) - .withRedirect(Redirect.NEVER) - .withExecutor(Objects.requireNonNull(httpClientExecutor)) - .withName("donation") - .withSecurityProtocol(FaultTolerantHttpClient.SECURITY_PROTOCOL_TLS_1_3) - .build(); } @Timed @@ -188,55 +142,4 @@ public class DonationController { }).thenCompose(Function.identity()); } - @Timed - @POST - @Path("/authorize-apple-pay") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - public CompletableFuture getApplePayAuthorization(@Auth AuthenticatedAccount auth, @NotNull @Valid ApplePayAuthorizationRequest request) { - if (!supportedCurrencies.contains(request.getCurrency())) { - return CompletableFuture.completedFuture(Response.status(422).build()); - } - - final Map formData = new HashMap<>(); - formData.put("amount", Long.toString(request.getAmount())); - formData.put("currency", request.getCurrency()); - if (!Strings.isNullOrEmpty(description)) { - formData.put("description", description); - } - final HttpRequest httpRequest = HttpRequest.newBuilder() - .uri(uri) - .POST(FormDataBodyPublisher.of(formData)) - .header("Authorization", "Basic " + Base64.getEncoder().encodeToString( - (apiKey + ":").getBytes(StandardCharsets.UTF_8))) - .header("Content-Type", "application/x-www-form-urlencoded") - .build(); - return httpClient.sendAsync(httpRequest, BodyHandlers.ofString()) - .thenApply(this::processApplePayAuthorizationRemoteResponse); - } - - private Response processApplePayAuthorizationRemoteResponse(HttpResponse response) { - ObjectMapper mapper = SystemMapper.getMapper(); - - if (response.statusCode() >= 200 && response.statusCode() < 300 && - MediaType.APPLICATION_JSON.equalsIgnoreCase(response.headers().firstValue("Content-Type").orElse(null))) { - try { - final JsonNode jsonResponse = mapper.readTree(response.body()); - final String id = jsonResponse.get("id").asText(null); - final String clientSecret = jsonResponse.get("client_secret").asText(null); - if (Strings.isNullOrEmpty(id) || Strings.isNullOrEmpty(clientSecret)) { - logger.warn("missing fields in json response in donation controller"); - return Response.status(500).build(); - } - final String responseJson = mapper.writeValueAsString(new ApplePayAuthorizationResponse(id, clientSecret)); - return Response.ok(responseJson, MediaType.APPLICATION_JSON_TYPE).build(); - } catch (JsonProcessingException e) { - logger.warn("json processing error in donation controller", e); - return Response.status(500).build(); - } - } else { - logger.warn("unexpected response code returned to donation controller"); - return Response.status(500).build(); - } - } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/ApplePayAuthorizationRequest.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/ApplePayAuthorizationRequest.java deleted file mode 100644 index d1097b3ce..000000000 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/ApplePayAuthorizationRequest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2021 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.textsecuregcm.entities; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.google.common.annotations.VisibleForTesting; -import javax.validation.constraints.Min; -import javax.validation.constraints.NotEmpty; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Size; - -public class ApplePayAuthorizationRequest { - - private String currency; - private long amount; - - @JsonProperty - @NotEmpty - @Size(min=3, max=3) - @Pattern(regexp="[a-z]{3}") - public String getCurrency() { - return currency; - } - - public void setCurrency(final String currency) { - this.currency = currency; - } - - @JsonProperty - @Min(0) - public long getAmount() { - return amount; - } - - @VisibleForTesting - public void setAmount(final long amount) { - this.amount = amount; - } -} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/ApplePayAuthorizationResponse.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/ApplePayAuthorizationResponse.java deleted file mode 100644 index 30db2da48..000000000 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/ApplePayAuthorizationResponse.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2021 Signal Messenger, LLC - * SPDX-License-Identifier: AGPL-3.0-only - */ - -package org.whispersystems.textsecuregcm.entities; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.dropwizard.util.Strings; -import javax.validation.constraints.NotEmpty; - -public class ApplePayAuthorizationResponse { - - private final String id; - private final String clientSecret; - - @JsonCreator - public ApplePayAuthorizationResponse( - @JsonProperty("id") final String id, - @JsonProperty("client_secret") final String clientSecret) { - if (Strings.isNullOrEmpty(id)) { - throw new IllegalArgumentException("id cannot be empty"); - } - if (Strings.isNullOrEmpty(clientSecret)) { - throw new IllegalArgumentException("clientSecret cannot be empty"); - } - - this.id = id; - this.clientSecret = clientSecret; - } - - @JsonProperty("id") - @NotEmpty - public String getId() { - return id; - } - - @JsonProperty("client_secret") - @NotEmpty - public String getClientSecret() { - return clientSecret; - } -} diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DonationControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DonationControllerTest.java index 94dd67b39..7de6e46d9 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DonationControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/DonationControllerTest.java @@ -5,9 +5,6 @@ package org.whispersystems.textsecuregcm.tests.controllers; -import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; -import static com.github.tomakehurst.wiremock.client.WireMock.post; -import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -27,10 +24,7 @@ import java.time.Instant; import java.util.List; import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; import javax.ws.rs.client.Entity; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; @@ -47,13 +41,7 @@ import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount; import org.whispersystems.textsecuregcm.configuration.BadgeConfiguration; import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration; -import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration; -import org.whispersystems.textsecuregcm.configuration.DonationConfiguration; -import org.whispersystems.textsecuregcm.configuration.RetryConfiguration; -import org.whispersystems.textsecuregcm.configuration.StripeConfiguration; import org.whispersystems.textsecuregcm.controllers.DonationController; -import org.whispersystems.textsecuregcm.entities.ApplePayAuthorizationRequest; -import org.whispersystems.textsecuregcm.entities.ApplePayAuthorizationResponse; import org.whispersystems.textsecuregcm.entities.BadgeSvg; import org.whispersystems.textsecuregcm.entities.RedeemReceiptRequest; import org.whispersystems.textsecuregcm.storage.AccountBadge; @@ -65,7 +53,6 @@ import org.whispersystems.textsecuregcm.util.TestClock; class DonationControllerTest { - private static final Executor httpClientExecutor = Executors.newSingleThreadExecutor(); private static final long nowEpochSeconds = 1_500_000_000L; @RegisterExtension @@ -75,20 +62,6 @@ class DonationControllerTest { private static final SecureRandom SECURE_RANDOM = new SecureRandom(); - static DonationConfiguration getDonationConfiguration() { - DonationConfiguration configuration = new DonationConfiguration(); - configuration.setDescription("some description"); - configuration.setUri("http://localhost:" + wm.getRuntimeInfo().getHttpPort() + "/foo/bar"); - configuration.setCircuitBreaker(new CircuitBreakerConfiguration()); - configuration.setRetry(new RetryConfiguration()); - configuration.setSupportedCurrencies(Set.of("usd", "gbp")); - return configuration; - } - - static StripeConfiguration getStripeConfiguration() { - return new StripeConfiguration("test-api-key", new byte[16], "Boost Description String"); - } - static BadgesConfiguration getBadgesConfiguration() { return new BadgesConfiguration( List.of( @@ -137,8 +110,7 @@ class DonationControllerTest { ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class))) .setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .addResource(new DonationController(clock, zkReceiptOperations, redeemedReceiptsManager, accountsManager, - getBadgesConfiguration(), receiptCredentialPresentationFactory, httpClientExecutor, - getDonationConfiguration(), getStripeConfiguration())) + getBadgesConfiguration(), receiptCredentialPresentationFactory)) .build(); resources.before(); } @@ -148,57 +120,6 @@ class DonationControllerTest { resources.after(); } - @Test - void testGetApplePayAuthorizationReturns200() { - wm.stubFor(post(urlEqualTo("/foo/bar")) - .withBasicAuth("test-api-key", "") - .willReturn(aResponse() - .withHeader("Content-Type", MediaType.APPLICATION_JSON) - .withBody("{\"id\":\"an_id\",\"client_secret\":\"some_secret\"}"))); - - ApplePayAuthorizationRequest request = new ApplePayAuthorizationRequest(); - request.setCurrency("usd"); - request.setAmount(1000); - Response response = resources.getJerseyTest() - .target("/v1/donation/authorize-apple-pay") - .request() - .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .post(Entity.entity(request, MediaType.APPLICATION_JSON_TYPE)); - - assertThat(response.getStatus()).isEqualTo(200); - - ApplePayAuthorizationResponse responseObject = response.readEntity(ApplePayAuthorizationResponse.class); - assertThat(responseObject.getId()).isEqualTo("an_id"); - assertThat(responseObject.getClientSecret()).isEqualTo("some_secret"); - } - - @Test - void testGetApplePayAuthorizationWithoutAuthHeaderReturns401() { - ApplePayAuthorizationRequest request = new ApplePayAuthorizationRequest(); - request.setCurrency("usd"); - request.setAmount(1000); - Response response = resources.getJerseyTest() - .target("/v1/donation/authorize-apple-pay") - .request() - .post(Entity.entity(request, MediaType.APPLICATION_JSON_TYPE)); - - assertThat(response.getStatus()).isEqualTo(401); - } - - @Test - void testGetApplePayAuthorizationWithUnsupportedCurrencyReturns422() { - ApplePayAuthorizationRequest request = new ApplePayAuthorizationRequest(); - request.setCurrency("zzz"); - request.setAmount(1000); - Response response = resources.getJerseyTest() - .target("/v1/donation/authorize-apple-pay") - .request() - .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) - .post(Entity.entity(request, MediaType.APPLICATION_JSON_TYPE)); - - assertThat(response.getStatus()).isEqualTo(422); - } - @Test void testRedeemReceipt() { when(receiptCredentialPresentation.getReceiptSerial()).thenReturn(receiptSerial); @@ -246,13 +167,13 @@ class DonationControllerTest { @Test void testRedeemReceiptBadCredentialPresentation() throws InvalidInputException { when(receiptCredentialPresentationFactory.build(any())).thenThrow(new InvalidInputException()); - + final Response response = resources.getJerseyTest() .target("/v1/donation/redeem-receipt") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) .post(Entity.entity(new RedeemReceiptRequest(presentation, true, true), MediaType.APPLICATION_JSON_TYPE)); - + assertThat(response.getStatus()).isEqualTo(400); } }