diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountControllerV2.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountControllerV2.java index c694a080f..4ce6d955a 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountControllerV2.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/AccountControllerV2.java @@ -32,6 +32,7 @@ import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse; import org.whispersystems.textsecuregcm.entities.ChangeNumberRequest; import org.whispersystems.textsecuregcm.entities.MismatchedDevices; +import org.whispersystems.textsecuregcm.entities.PhoneNumberDiscoverabilityRequest; import org.whispersystems.textsecuregcm.entities.PhoneVerificationRequest; import org.whispersystems.textsecuregcm.entities.StaleDevices; import org.whispersystems.textsecuregcm.limits.RateLimiter; @@ -129,4 +130,16 @@ public class AccountControllerV2 { } } + @Timed + @PUT + @Path("/phone_number_discoverability") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + public void setPhoneNumberDiscoverability( + @Auth AuthenticatedAccount auth, + @NotNull @Valid PhoneNumberDiscoverabilityRequest phoneNumberDiscoverability + ) { + accountsManager.update(auth.getAccount(), a -> a.setDiscoverableByPhoneNumber( + phoneNumberDiscoverability.discoverableByPhoneNumber())); + } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/PhoneNumberDiscoverabilityRequest.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/PhoneNumberDiscoverabilityRequest.java new file mode 100644 index 000000000..ba0e52a2f --- /dev/null +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/PhoneNumberDiscoverabilityRequest.java @@ -0,0 +1,5 @@ +package org.whispersystems.textsecuregcm.entities; + +import javax.validation.constraints.NotNull; + +public record PhoneNumberDiscoverabilityRequest(@NotNull Boolean discoverableByPhoneNumber) {} diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/AccountControllerV2Test.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/AccountControllerV2Test.java index f336076ca..41abe473f 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/AccountControllerV2Test.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/AccountControllerV2Test.java @@ -5,13 +5,16 @@ package org.whispersystems.textsecuregcm.controllers; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -46,6 +49,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.EnumSource; import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.ArgumentCaptor; import org.mockito.stubbing.Answer; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount; @@ -54,6 +58,7 @@ import org.whispersystems.textsecuregcm.auth.RegistrationLockError; import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager; import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse; import org.whispersystems.textsecuregcm.entities.ChangeNumberRequest; +import org.whispersystems.textsecuregcm.entities.PhoneNumberDiscoverabilityRequest; import org.whispersystems.textsecuregcm.entities.RegistrationSession; import org.whispersystems.textsecuregcm.limits.RateLimiter; import org.whispersystems.textsecuregcm.limits.RateLimiters; @@ -66,6 +71,7 @@ import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.ChangeNumberManager; import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.storage.RegistrationRecoveryPasswordsManager; +import org.whispersystems.textsecuregcm.tests.util.AccountsHelper; import org.whispersystems.textsecuregcm.tests.util.AuthHelper; import org.whispersystems.textsecuregcm.util.SystemMapper; @@ -393,4 +399,43 @@ class AccountControllerV2Test { } } + @Nested + class PhoneNumberDiscoverability { + + @BeforeEach + void setup() { + AccountsHelper.setupMockUpdate(accountsManager); + } + @Test + void testSetPhoneNumberDiscoverability() { + Response response = resources.getJerseyTest() + .target("/v2/accounts/phone_number_discoverability") + .request() + .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) + .put(Entity.json(new PhoneNumberDiscoverabilityRequest(true))); + + assertThat(response.getStatus()).isEqualTo(204); + + ArgumentCaptor discoverabilityCapture = ArgumentCaptor.forClass(Boolean.class); + verify(AuthHelper.VALID_ACCOUNT).setDiscoverableByPhoneNumber(discoverabilityCapture.capture()); + assertThat(discoverabilityCapture.getValue()).isTrue(); + } + + @Test + void testSetNullPhoneNumberDiscoverability() { + Response response = resources.getJerseyTest() + .target("/v2/accounts/phone_number_discoverability") + .request() + .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) + .put(Entity.json( + """ + { + "discoverableByPhoneNumber": null + } + """)); + + assertThat(response.getStatus()).isEqualTo(422); + verify(AuthHelper.VALID_ACCOUNT, never()).setDiscoverableByPhoneNumber(anyBoolean()); + } + } }