Block attempts to set wallet addresses from unsupported countries.

This commit is contained in:
Jon Chambers 2021-03-26 11:53:57 -04:00 committed by Jon Chambers
parent 5965f0fd22
commit 7e29ed1cc7
3 changed files with 74 additions and 11 deletions

View File

@ -409,7 +409,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
AttachmentControllerV3 attachmentControllerV3 = new AttachmentControllerV3(rateLimiters, config.getGcpAttachmentsConfiguration().getDomain(), config.getGcpAttachmentsConfiguration().getEmail(), config.getGcpAttachmentsConfiguration().getMaxSizeInBytes(), config.getGcpAttachmentsConfiguration().getPathPrefix(), config.getGcpAttachmentsConfiguration().getRsaSigningKey()); AttachmentControllerV3 attachmentControllerV3 = new AttachmentControllerV3(rateLimiters, config.getGcpAttachmentsConfiguration().getDomain(), config.getGcpAttachmentsConfiguration().getEmail(), config.getGcpAttachmentsConfiguration().getMaxSizeInBytes(), config.getGcpAttachmentsConfiguration().getPathPrefix(), config.getGcpAttachmentsConfiguration().getRsaSigningKey());
KeysController keysController = new KeysController(rateLimiters, keysDynamoDb, accountsManager, directoryQueue); KeysController keysController = new KeysController(rateLimiters, keysDynamoDb, accountsManager, directoryQueue);
MessageController messageController = new MessageController(rateLimiters, messageSender, receiptSender, accountsManager, messagesManager, apnFallbackManager, dynamicConfigurationManager, metricsCluster, declinedMessageReceiptExecutor); MessageController messageController = new MessageController(rateLimiters, messageSender, receiptSender, accountsManager, messagesManager, apnFallbackManager, dynamicConfigurationManager, metricsCluster, declinedMessageReceiptExecutor);
ProfileController profileController = new ProfileController(rateLimiters, accountsManager, profilesManager, usernamesManager, cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner, config.getCdnConfiguration().getBucket(), zkProfileOperations, isZkEnabled); ProfileController profileController = new ProfileController(rateLimiters, accountsManager, profilesManager, usernamesManager, dynamicConfigurationManager, cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner, config.getCdnConfiguration().getBucket(), zkProfileOperations, isZkEnabled);
StickerController stickerController = new StickerController(rateLimiters, config.getCdnConfiguration().getAccessKey(), config.getCdnConfiguration().getAccessSecret(), config.getCdnConfiguration().getRegion(), config.getCdnConfiguration().getBucket()); StickerController stickerController = new StickerController(rateLimiters, config.getCdnConfiguration().getAccessKey(), config.getCdnConfiguration().getAccessSecret(), config.getCdnConfiguration().getRegion(), config.getCdnConfiguration().getBucket());
RemoteConfigController remoteConfigController = new RemoteConfigController(remoteConfigsManager, config.getRemoteConfigConfiguration().getAuthorizedTokens(), config.getRemoteConfigConfiguration().getGlobalConfig()); RemoteConfigController remoteConfigController = new RemoteConfigController(remoteConfigsManager, config.getRemoteConfigConfiguration().getAuthorizedTokens(), config.getRemoteConfigConfiguration().getGlobalConfig());

View File

@ -12,6 +12,7 @@ import java.security.SecureRandom;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.valueextraction.Unwrapping; import javax.validation.valueextraction.Unwrapping;
@ -26,9 +27,11 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException; import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.VerificationFailedException; import org.signal.zkgroup.VerificationFailedException;
import org.signal.zkgroup.profiles.ProfileKeyCommitment; import org.signal.zkgroup.profiles.ProfileKeyCommitment;
@ -50,11 +53,13 @@ import org.whispersystems.textsecuregcm.s3.PolicySigner;
import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator; import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator;
import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
import org.whispersystems.textsecuregcm.storage.ProfilesManager; import org.whispersystems.textsecuregcm.storage.ProfilesManager;
import org.whispersystems.textsecuregcm.storage.UsernamesManager; import org.whispersystems.textsecuregcm.storage.UsernamesManager;
import org.whispersystems.textsecuregcm.storage.VersionedProfile; import org.whispersystems.textsecuregcm.storage.VersionedProfile;
import org.whispersystems.textsecuregcm.util.ExactlySize; import org.whispersystems.textsecuregcm.util.ExactlySize;
import org.whispersystems.textsecuregcm.util.Pair; import org.whispersystems.textsecuregcm.util.Pair;
import org.whispersystems.textsecuregcm.util.Util;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType") @SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@Path("/v1/profile") @Path("/v1/profile")
@ -66,6 +71,7 @@ public class ProfileController {
private final ProfilesManager profilesManager; private final ProfilesManager profilesManager;
private final AccountsManager accountsManager; private final AccountsManager accountsManager;
private final UsernamesManager usernamesManager; private final UsernamesManager usernamesManager;
private final DynamicConfigurationManager dynamicConfigurationManager;
private final PolicySigner policySigner; private final PolicySigner policySigner;
private final PostPolicyGenerator policyGenerator; private final PostPolicyGenerator policyGenerator;
@ -76,20 +82,22 @@ public class ProfileController {
private final String bucket; private final String bucket;
public ProfileController(RateLimiters rateLimiters, public ProfileController(RateLimiters rateLimiters,
AccountsManager accountsManager, AccountsManager accountsManager,
ProfilesManager profilesManager, ProfilesManager profilesManager,
UsernamesManager usernamesManager, UsernamesManager usernamesManager,
AmazonS3 s3client, DynamicConfigurationManager dynamicConfigurationManager,
PostPolicyGenerator policyGenerator, AmazonS3 s3client,
PolicySigner policySigner, PostPolicyGenerator policyGenerator,
String bucket, PolicySigner policySigner,
ServerZkProfileOperations zkProfileOperations, String bucket,
boolean isZkEnabled) ServerZkProfileOperations zkProfileOperations,
boolean isZkEnabled)
{ {
this.rateLimiters = rateLimiters; this.rateLimiters = rateLimiters;
this.accountsManager = accountsManager; this.accountsManager = accountsManager;
this.profilesManager = profilesManager; this.profilesManager = profilesManager;
this.usernamesManager = usernamesManager; this.usernamesManager = usernamesManager;
this.dynamicConfigurationManager = dynamicConfigurationManager;
this.zkProfileOperations = zkProfileOperations; this.zkProfileOperations = zkProfileOperations;
this.bucket = bucket; this.bucket = bucket;
this.s3client = s3client; this.s3client = s3client;
@ -105,6 +113,15 @@ public class ProfileController {
public Response setProfile(@Auth Account account, @Valid CreateProfileRequest request) { public Response setProfile(@Auth Account account, @Valid CreateProfileRequest request) {
if (!isZkEnabled) throw new WebApplicationException(Response.Status.NOT_FOUND); if (!isZkEnabled) throw new WebApplicationException(Response.Status.NOT_FOUND);
final Set<String> allowedPaymentsCountryCodes =
dynamicConfigurationManager.getConfiguration().getPaymentsConfiguration().getAllowedCountryCodes();
if (StringUtils.isNotBlank(request.getPaymentAddress()) &&
!allowedPaymentsCountryCodes.contains(Util.getCountryCode(account.getNumber()))) {
return Response.status(Status.FORBIDDEN).build();
}
Optional<VersionedProfile> currentProfile = profilesManager.get(account.getUuid(), request.getVersion()); Optional<VersionedProfile> currentProfile = profilesManager.get(account.getUuid(), request.getVersion());
String avatar = request.isAvatar() ? generateAvatarObjectName() : null; String avatar = request.isAvatar() ? generateAvatarObjectName() : null;
Optional<ProfileAvatarUploadAttributes> response = Optional.empty(); Optional<ProfileAvatarUploadAttributes> response = Optional.empty();

View File

@ -11,6 +11,7 @@ import static org.mockito.Mockito.argThat;
import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.eq; import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset; import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@ -21,7 +22,9 @@ import com.amazonaws.services.s3.AmazonS3;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit.ResourceTestRule; import io.dropwizard.testing.junit.ResourceTestRule;
import java.util.Collections;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import javax.ws.rs.client.Entity; import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
@ -38,6 +41,8 @@ import org.signal.zkgroup.profiles.ProfileKeyCommitment;
import org.signal.zkgroup.profiles.ServerZkProfileOperations; import org.signal.zkgroup.profiles.ServerZkProfileOperations;
import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier; import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount; import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicPaymentsConfiguration;
import org.whispersystems.textsecuregcm.controllers.ProfileController; import org.whispersystems.textsecuregcm.controllers.ProfileController;
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException; import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
import org.whispersystems.textsecuregcm.entities.CreateProfileRequest; import org.whispersystems.textsecuregcm.entities.CreateProfileRequest;
@ -49,11 +54,13 @@ import org.whispersystems.textsecuregcm.s3.PolicySigner;
import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator; import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator;
import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.DynamicConfigurationManager;
import org.whispersystems.textsecuregcm.storage.ProfilesManager; import org.whispersystems.textsecuregcm.storage.ProfilesManager;
import org.whispersystems.textsecuregcm.storage.UsernamesManager; import org.whispersystems.textsecuregcm.storage.UsernamesManager;
import org.whispersystems.textsecuregcm.storage.VersionedProfile; import org.whispersystems.textsecuregcm.storage.VersionedProfile;
import org.whispersystems.textsecuregcm.tests.util.AuthHelper; import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
import org.whispersystems.textsecuregcm.util.SystemMapper; import org.whispersystems.textsecuregcm.util.SystemMapper;
import org.whispersystems.textsecuregcm.util.Util;
public class ProfileControllerTest { public class ProfileControllerTest {
@ -69,6 +76,9 @@ public class ProfileControllerTest {
private static PolicySigner policySigner = new PolicySigner("accessSecret", "us-west-1"); private static PolicySigner policySigner = new PolicySigner("accessSecret", "us-west-1");
private static ServerZkProfileOperations zkProfileOperations = mock(ServerZkProfileOperations.class); private static ServerZkProfileOperations zkProfileOperations = mock(ServerZkProfileOperations.class);
private static DynamicConfigurationManager dynamicConfigurationManager = mock(DynamicConfigurationManager.class);
private DynamicPaymentsConfiguration dynamicPaymentsConfiguration;
private Account profileAccount; private Account profileAccount;
@ -82,6 +92,7 @@ public class ProfileControllerTest {
accountsManager, accountsManager,
profilesManager, profilesManager,
usernamesManager, usernamesManager,
dynamicConfigurationManager,
s3client, s3client,
postPolicyGenerator, postPolicyGenerator,
policySigner, policySigner,
@ -94,6 +105,13 @@ public class ProfileControllerTest {
public void setup() throws Exception { public void setup() throws Exception {
reset(s3client); reset(s3client);
dynamicPaymentsConfiguration = mock(DynamicPaymentsConfiguration.class);
final DynamicConfiguration dynamicConfiguration = mock(DynamicConfiguration.class);
when(dynamicConfigurationManager.getConfiguration()).thenReturn(dynamicConfiguration);
when(dynamicConfiguration.getPaymentsConfiguration()).thenReturn(dynamicPaymentsConfiguration);
when(dynamicPaymentsConfiguration.getAllowedCountryCodes()).thenReturn(Collections.emptySet());
when(rateLimiters.getProfileLimiter()).thenReturn(rateLimiter); when(rateLimiters.getProfileLimiter()).thenReturn(rateLimiter);
when(rateLimiters.getUsernameLookupLimiter()).thenReturn(usernameRateLimiter); when(rateLimiters.getUsernameLookupLimiter()).thenReturn(usernameRateLimiter);
@ -365,7 +383,8 @@ public class ProfileControllerTest {
assertThat(profileArgumentCaptor.getValue().getVersion()).isEqualTo("anotherversion"); assertThat(profileArgumentCaptor.getValue().getVersion()).isEqualTo("anotherversion");
assertThat(profileArgumentCaptor.getValue().getName()).isEqualTo("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"); assertThat(profileArgumentCaptor.getValue().getName()).isEqualTo("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678");
assertThat(profileArgumentCaptor.getValue().getAboutEmoji()).isNull(); assertThat(profileArgumentCaptor.getValue().getAboutEmoji()).isNull();
assertThat(profileArgumentCaptor.getValue().getAbout()).isNull(); } assertThat(profileArgumentCaptor.getValue().getAbout()).isNull();
}
@Test @Test
public void testSetProfileWithAvatarUploadAndPreviousAvatar() throws InvalidInputException { public void testSetProfileWithAvatarUploadAndPreviousAvatar() throws InvalidInputException {
@ -458,6 +477,9 @@ public class ProfileControllerTest {
@Test @Test
public void testSetProfilePaymentAddress() throws InvalidInputException { public void testSetProfilePaymentAddress() throws InvalidInputException {
when(dynamicPaymentsConfiguration.getAllowedCountryCodes())
.thenReturn(Set.of(Util.getCountryCode(AuthHelper.VALID_NUMBER_TWO)));
ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID); ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID);
clearInvocations(AuthHelper.VALID_ACCOUNT_TWO); clearInvocations(AuthHelper.VALID_ACCOUNT_TWO);
@ -494,6 +516,27 @@ public class ProfileControllerTest {
assertThat(profile.getPaymentAddress()).isEqualTo(paymentAddress); assertThat(profile.getPaymentAddress()).isEqualTo(paymentAddress);
} }
@Test
public void testSetProfilePaymentAddressCountryNotAllowed() throws InvalidInputException {
ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID);
clearInvocations(AuthHelper.VALID_ACCOUNT_TWO);
final String name = RandomStringUtils.randomAlphabetic(380);
final String paymentAddress = RandomStringUtils.randomAlphanumeric(684);
Response response = resources.getJerseyTest()
.target("/v1/profile")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER_TWO, AuthHelper.VALID_PASSWORD_TWO))
.put(Entity.entity(new CreateProfileRequest(commitment, "yetanotherversion", name, null, null, paymentAddress, false), MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(403);
assertThat(response.hasEntity()).isFalse();
verify(profilesManager, never()).set(any(), any());
}
@Test @Test
public void testGetProfileByVersion() throws RateLimitExceededException { public void testGetProfileByVersion() throws RateLimitExceededException {
Profile profile = resources.getJerseyTest() Profile profile = resources.getJerseyTest()
@ -521,6 +564,9 @@ public class ProfileControllerTest {
@Test @Test
public void testSetProfileUpdatesAccountCurrentVersion() throws InvalidInputException { public void testSetProfileUpdatesAccountCurrentVersion() throws InvalidInputException {
when(dynamicPaymentsConfiguration.getAllowedCountryCodes())
.thenReturn(Set.of(Util.getCountryCode(AuthHelper.VALID_NUMBER_TWO)));
ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID_TWO); ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID_TWO);
clearInvocations(AuthHelper.VALID_ACCOUNT_TWO); clearInvocations(AuthHelper.VALID_ACCOUNT_TWO);