username links API

This commit is contained in:
Sergey Skrobotov
2023-06-02 10:15:09 -07:00
parent ecd207f0a1
commit 47cc7fd615
13 changed files with 653 additions and 142 deletions

View File

@@ -15,6 +15,7 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
@@ -54,6 +55,7 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import javax.ws.rs.client.Entity;
import javax.ws.rs.client.Invocation;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.commons.lang3.RandomUtils;
@@ -93,6 +95,7 @@ import org.whispersystems.textsecuregcm.entities.AccountIdentityResponse;
import org.whispersystems.textsecuregcm.entities.ApnRegistrationId;
import org.whispersystems.textsecuregcm.entities.ChangePhoneNumberRequest;
import org.whispersystems.textsecuregcm.entities.ConfirmUsernameHashRequest;
import org.whispersystems.textsecuregcm.entities.EncryptedUsername;
import org.whispersystems.textsecuregcm.entities.GcmRegistrationId;
import org.whispersystems.textsecuregcm.entities.IncomingMessage;
import org.whispersystems.textsecuregcm.entities.RegistrationLock;
@@ -1081,7 +1084,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/code/1234")
.request()
.header("Authorization", AuthHelper.getProvisioningAuthHeader(SENDER, "bar"))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getProvisioningAuthHeader(SENDER, "bar"))
.put(Entity.entity(new AccountAttributes(), MediaType.APPLICATION_JSON_TYPE), AccountIdentityResponse.class);
verify(accountsManager).create(eq(SENDER), eq("bar"), any(), any(), anyList());
@@ -1094,7 +1097,7 @@ class AccountControllerTest {
final Response response = resources.getJerseyTest()
.target("/v1/accounts/code/1234")
.request()
.header("Authorization", "This is not a valid authorization header")
.header(HttpHeaders.AUTHORIZATION, "This is not a valid authorization header")
.put(Entity.entity(new AccountAttributes(), MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(401);
@@ -1106,7 +1109,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/code/1234")
.request()
.header("Authorization", AuthHelper.getProvisioningAuthHeader(SENDER_OLD, "bar"))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getProvisioningAuthHeader(SENDER_OLD, "bar"))
.put(Entity.entity(new AccountAttributes(false, 2222, null, null, true, null),
MediaType.APPLICATION_JSON_TYPE));
@@ -1130,7 +1133,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/code/1111")
.request()
.header("Authorization", AuthHelper.getProvisioningAuthHeader(SENDER, "bar"))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getProvisioningAuthHeader(SENDER, "bar"))
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, true, null),
MediaType.APPLICATION_JSON_TYPE));
@@ -1155,7 +1158,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/code/666666")
.request()
.header("Authorization", AuthHelper.getProvisioningAuthHeader(SENDER_REG_LOCK, "bar"))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getProvisioningAuthHeader(SENDER_REG_LOCK, "bar"))
.put(Entity.entity(
new AccountAttributes(false, 3333, null, HexFormat.of().formatHex(registration_lock_key), true, null),
MediaType.APPLICATION_JSON_TYPE), AccountIdentityResponse.class);
@@ -1180,7 +1183,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/code/666666")
.request()
.header("Authorization", AuthHelper.getProvisioningAuthHeader(SENDER_REG_LOCK, "bar"))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getProvisioningAuthHeader(SENDER_REG_LOCK, "bar"))
.put(Entity.entity(
new AccountAttributes(false, 3333, null, HexFormat.of().formatHex(registration_lock_key), true, null),
MediaType.APPLICATION_JSON_TYPE), AccountIdentityResponse.class);
@@ -1214,7 +1217,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/code/666666")
.request()
.header("Authorization", AuthHelper.getProvisioningAuthHeader(SENDER_REG_LOCK, "bar"))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getProvisioningAuthHeader(SENDER_REG_LOCK, "bar"))
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, true, null),
MediaType.APPLICATION_JSON_TYPE), AccountIdentityResponse.class);
@@ -1241,7 +1244,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/code/666666")
.request()
.header("Authorization", AuthHelper.getProvisioningAuthHeader(SENDER_REG_LOCK, "bar"))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getProvisioningAuthHeader(SENDER_REG_LOCK, "bar"))
.put(Entity.entity(new AccountAttributes(false, 3333, null,
HexFormat.of().formatHex(new byte[32]), true, null),
MediaType.APPLICATION_JSON_TYPE));
@@ -1268,7 +1271,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/code/666666")
.request()
.header("Authorization", AuthHelper.getProvisioningAuthHeader(SENDER_REG_LOCK, "bar"))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getProvisioningAuthHeader(SENDER_REG_LOCK, "bar"))
.put(Entity.entity(new AccountAttributes(false, 3333, null, null, true, null),
MediaType.APPLICATION_JSON_TYPE));
@@ -1305,7 +1308,7 @@ class AccountControllerTest {
.target("/v1/accounts/code/1234")
.queryParam("transfer", true)
.request()
.header("Authorization", AuthHelper.getProvisioningAuthHeader(SENDER_TRANSFER, "bar"))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getProvisioningAuthHeader(SENDER_TRANSFER, "bar"))
.put(Entity.entity(new AccountAttributes(false, 2222, null, null, true, null),
MediaType.APPLICATION_JSON_TYPE));
@@ -1329,7 +1332,7 @@ class AccountControllerTest {
.target("/v1/accounts/code/1234")
.queryParam("transfer", true)
.request()
.header("Authorization", AuthHelper.getProvisioningAuthHeader(SENDER_TRANSFER, "bar"))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getProvisioningAuthHeader(SENDER_TRANSFER, "bar"))
.put(Entity.entity(new AccountAttributes(false, 2222, null, null, true, null),
MediaType.APPLICATION_JSON_TYPE));
@@ -1352,7 +1355,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/code/1234")
.request()
.header("Authorization", AuthHelper.getProvisioningAuthHeader(SENDER_TRANSFER, "bar"))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getProvisioningAuthHeader(SENDER_TRANSFER, "bar"))
.put(Entity.entity(new AccountAttributes(false, 2222, null, null, true, null),
MediaType.APPLICATION_JSON_TYPE));
@@ -1375,7 +1378,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/number")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null, null, null, null, null),
MediaType.APPLICATION_JSON_TYPE), AccountIdentityResponse.class);
@@ -1397,7 +1400,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/number")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null, null, null, null, null),
MediaType.APPLICATION_JSON_TYPE));
@@ -1415,7 +1418,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/number")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null, null, null, null, null),
MediaType.APPLICATION_JSON_TYPE));
@@ -1434,7 +1437,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/number")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.entity(new ChangePhoneNumberRequest(AuthHelper.VALID_NUMBER, "567890", null, null, null, null, null, null),
MediaType.APPLICATION_JSON_TYPE), AccountIdentityResponse.class);
@@ -1452,7 +1455,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/number")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null, null, null, null, null),
MediaType.APPLICATION_JSON_TYPE));
@@ -1476,7 +1479,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/number")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null, null, null, null, null),
MediaType.APPLICATION_JSON_TYPE));
@@ -1512,7 +1515,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/number")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null, null, null, null, null),
MediaType.APPLICATION_JSON_TYPE));
@@ -1547,7 +1550,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/number")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.entity(new ChangePhoneNumberRequest(number, code, null, null, null, null, null, null),
MediaType.APPLICATION_JSON_TYPE));
@@ -1587,7 +1590,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/number")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.entity(new ChangePhoneNumberRequest(number, code, reglock, null, null, null, null, null),
MediaType.APPLICATION_JSON_TYPE));
@@ -1626,7 +1629,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/number")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.entity(new ChangePhoneNumberRequest(number, code, reglock, null, null, null, null, null),
MediaType.APPLICATION_JSON_TYPE));
@@ -1675,7 +1678,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/number")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.entity(new ChangePhoneNumberRequest(
number, code, null,
pniIdentityKey, deviceMessages,
@@ -1729,7 +1732,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/number")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.entity(new ChangePhoneNumberRequest(
AuthHelper.VALID_NUMBER, code, null,
pniIdentityKey, deviceMessages,
@@ -1754,7 +1757,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/registration_lock/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(new RegistrationLock("1234567890123456789012345678901234567890123456789012345678901234")));
assertThat(response.getStatus()).isEqualTo(204);
@@ -1776,7 +1779,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/registration_lock/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(new RegistrationLock("313")));
assertThat(response.getStatus()).isEqualTo(422);
@@ -1788,7 +1791,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/registration_lock/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.put(Entity.json(new RegistrationLock("1234567890123456789012345678901234567890123456789012345678901234")));
assertThat(response.getStatus()).isEqualTo(401);
@@ -1800,7 +1803,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/gcm/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.put(Entity.json(new GcmRegistrationId("z000")));
assertThat(response.getStatus()).isEqualTo(204);
@@ -1815,7 +1818,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/gcm/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.put(Entity.json("{}"));
assertThat(response.getStatus()).isEqualTo(422);
@@ -1828,7 +1831,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/apn/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.put(Entity.json(new ApnRegistrationId("first", "second")));
assertThat(response.getStatus()).isEqualTo(204);
@@ -1844,7 +1847,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/apn/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.put(Entity.json(new ApnRegistrationId("first", null)));
assertThat(response.getStatus()).isEqualTo(204);
@@ -1870,7 +1873,7 @@ class AccountControllerTest {
final Response response = resources.getJerseyTest()
.target(path)
.request()
.header("Authorization", AuthHelper.getAuthHeader(aci, password))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(aci, password))
.get();
assertThat(response.getStatus()).isEqualTo(expectedHttpStatusCode);
@@ -1889,6 +1892,145 @@ class AccountControllerTest {
);
}
static Stream<Arguments> testSetUsernameLink() {
return Stream.of(
Arguments.of(false, true, true, 32, 401),
Arguments.of(true, true, false, 32, 409),
Arguments.of(true, true, true, 129, 422),
Arguments.of(true, true, true, 0, 422),
Arguments.of(true, false, true, 32, 429),
Arguments.of(true, true, true, 128, 200)
);
}
@ParameterizedTest
@MethodSource
public void testSetUsernameLink(
final boolean auth,
final boolean passRateLimiting,
final boolean setUsernameHash,
final int payloadSize,
final int expectedStatus) throws Exception {
// checking if rate limiting needs to pass or fail for this test
if (passRateLimiting) {
MockUtils.updateRateLimiterResponseToAllow(
rateLimiters, RateLimiters.For.USERNAME_LINK_OPERATION, AuthHelper.VALID_UUID);
} else {
MockUtils.updateRateLimiterResponseToFail(
rateLimiters, RateLimiters.For.USERNAME_LINK_OPERATION, AuthHelper.VALID_UUID, Duration.ofMinutes(10), false);
}
// checking if username is to be set for this test
if (setUsernameHash) {
when(AuthHelper.VALID_ACCOUNT.getUsernameHash()).thenReturn(Optional.of(USERNAME_HASH_1));
} else {
when(AuthHelper.VALID_ACCOUNT.getUsernameHash()).thenReturn(Optional.empty());
}
final Invocation.Builder builder = resources.getJerseyTest()
.target("/v1/accounts/username_link")
.request();
// checking if auth is needed for this test
if (auth) {
builder.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD));
}
// make sure `update()` works
doReturn(AuthHelper.VALID_ACCOUNT).when(accountsManager).update(any(), any());
final Response put = builder.put(Entity.json(new EncryptedUsername(RandomUtils.nextBytes(payloadSize))));
assertEquals(expectedStatus, put.getStatus());
}
static Stream<Arguments> testDeleteUsernameLink() {
return Stream.of(
Arguments.of(false, true, 401),
Arguments.of(true, false, 429),
Arguments.of(true, true, 204)
);
}
@ParameterizedTest
@MethodSource
public void testDeleteUsernameLink(
final boolean auth,
final boolean passRateLimiting,
final int expectedStatus) throws Exception {
// checking if rate limiting needs to pass or fail for this test
if (passRateLimiting) {
MockUtils.updateRateLimiterResponseToAllow(
rateLimiters, RateLimiters.For.USERNAME_LINK_OPERATION, AuthHelper.VALID_UUID);
} else {
MockUtils.updateRateLimiterResponseToFail(
rateLimiters, RateLimiters.For.USERNAME_LINK_OPERATION, AuthHelper.VALID_UUID, Duration.ofMinutes(10), false);
}
final Invocation.Builder builder = resources.getJerseyTest()
.target("/v1/accounts/username_link")
.request();
// checking if auth is needed for this test
if (auth) {
builder.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD));
}
// make sure `update()` works
doReturn(AuthHelper.VALID_ACCOUNT).when(accountsManager).update(any(), any());
final Response delete = builder.delete();
assertEquals(expectedStatus, delete.getStatus());
}
static Stream<Arguments> testLookupUsernameLink() {
return Stream.of(
Arguments.of(false, true, true, true, 403),
Arguments.of(true, false, true, true, 429),
Arguments.of(true, true, false, true, 404),
Arguments.of(true, true, true, false, 404),
Arguments.of(true, true, true, true, 200)
);
}
@ParameterizedTest
@MethodSource
public void testLookupUsernameLink(
final boolean stayUnauthenticated,
final boolean passRateLimiting,
final boolean validUuidInput,
final boolean locateLinkByUuid,
final int expectedStatus) throws Exception {
MockUtils.updateRateLimiterResponseToAllow(
rateLimiters, RateLimiters.For.USERNAME_LINK_LOOKUP_PER_IP, NICE_HOST);
MockUtils.updateRateLimiterResponseToFail(
rateLimiters, RateLimiters.For.USERNAME_LINK_LOOKUP_PER_IP, RATE_LIMITED_IP_HOST, Duration.ofMinutes(10), false);
final String uuid = validUuidInput ? UUID.randomUUID().toString() : "invalid-uuid";
if (validUuidInput && locateLinkByUuid) {
final Account account = mock(Account.class);
doReturn(Optional.of(RandomUtils.nextBytes(16))).when(account).getEncryptedUsername();
doReturn(Optional.of(account)).when(accountsManager).getByUsernameLinkHandle(eq(UUID.fromString(uuid)));
}
final Invocation.Builder builder = resources.getJerseyTest()
.target("/v1/accounts/username_link/" + uuid)
.request();
if (!stayUnauthenticated) {
builder.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD));
}
final Response get = builder
.header(HttpHeaders.X_FORWARDED_FOR, passRateLimiting ? NICE_HOST : RATE_LIMITED_IP_HOST)
.get();
assertEquals(expectedStatus, get.getStatus());
}
@Test
void testReserveUsernameHash() throws UsernameHashNotAvailableException {
when(accountsManager.reserveUsernameHash(any(), any()))
@@ -1897,7 +2039,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/username_hash/reserve")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(new ReserveUsernameHashRequest(List.of(USERNAME_HASH_1, USERNAME_HASH_2))));
assertThat(response.getStatus()).isEqualTo(200);
assertThat(response.readEntity(ReserveUsernameHashResponse.class))
@@ -1912,7 +2054,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/username_hash/reserve")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(new ReserveUsernameHashRequest(List.of(USERNAME_HASH_1, USERNAME_HASH_2))));
assertThat(response.getStatus()).isEqualTo(409);
}
@@ -1924,7 +2066,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/username_hash/reserve")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(new ReserveUsernameHashRequest(usernameHashes)));
assertThat(response.getStatus()).isEqualTo(422);
}
@@ -1943,7 +2085,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/username_hash/reserve")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(new ReserveUsernameHashRequest(usernameHashes)));
assertThat(response.getStatus()).isEqualTo(422);
}
@@ -1954,7 +2096,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/username_hash/reserve")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(new ReserveUsernameHashRequest(null)));
assertThat(response.getStatus()).isEqualTo(422);
}
@@ -1965,7 +2107,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/username_hash/reserve")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(
// Has '+' and '='characters which are invalid in base64url
"""
@@ -1986,7 +2128,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/username_hash/confirm")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(new ConfirmUsernameHashRequest(USERNAME_HASH_1, ZK_PROOF)));
assertThat(response.getStatus()).isEqualTo(200);
assertArrayEquals(response.readEntity(UsernameHashResponse.class).usernameHash(), USERNAME_HASH_1);
@@ -2002,7 +2144,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/username_hash/confirm")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(new ConfirmUsernameHashRequest(USERNAME_HASH_1, ZK_PROOF)));
assertThat(response.getStatus()).isEqualTo(409);
verify(usernameZkProofVerifier).verifyProof(ZK_PROOF, USERNAME_HASH_1);
@@ -2017,7 +2159,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/username_hash/confirm")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(new ConfirmUsernameHashRequest(USERNAME_HASH_1, ZK_PROOF)));
assertThat(response.getStatus()).isEqualTo(410);
verify(usernameZkProofVerifier).verifyProof(ZK_PROOF, USERNAME_HASH_1);
@@ -2029,7 +2171,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/username_hash/confirm")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(
// Has '+' and '='characters which are invalid in base64url
"""
@@ -2049,7 +2191,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/username_hash/confirm")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(new ConfirmUsernameHashRequest(usernameHash, ZK_PROOF)));
assertThat(response.getStatus()).isEqualTo(422);
verifyNoInteractions(usernameZkProofVerifier);
@@ -2062,7 +2204,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/username_hash/confirm")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(new ConfirmUsernameHashRequest(USERNAME_HASH_1, ZK_PROOF)));
assertThat(response.getStatus()).isEqualTo(422);
verify(usernameZkProofVerifier).verifyProof(ZK_PROOF, USERNAME_HASH_1);
@@ -2074,7 +2216,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/username_hash/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.delete();
assertThat(response.getStatus()).isEqualTo(204);
@@ -2087,7 +2229,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/username_hash/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.INVALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.INVALID_PASSWORD))
.delete();
assertThat(response.getStatus()).isEqualTo(401);
@@ -2099,7 +2241,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/attributes/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(new AccountAttributes(false, 2222, null, null, true, null)));
assertThat(response.getStatus()).isEqualTo(204);
@@ -2111,7 +2253,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/attributes/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.UNDISCOVERABLE_UUID, AuthHelper.UNDISCOVERABLE_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.UNDISCOVERABLE_UUID, AuthHelper.UNDISCOVERABLE_PASSWORD))
.put(Entity.json(new AccountAttributes(false, 2222, null, null, true, null)));
assertThat(response.getStatus()).isEqualTo(204);
@@ -2124,7 +2266,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/attributes/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.UNDISCOVERABLE_UUID, AuthHelper.UNDISCOVERABLE_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.UNDISCOVERABLE_UUID, AuthHelper.UNDISCOVERABLE_PASSWORD))
.put(Entity.json(new AccountAttributes(false, 2222, null, null, true, null)
.withRecoveryPassword(recoveryPassword)));
@@ -2138,7 +2280,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/attributes/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(new AccountAttributes(false, 2222, null, null, false, null)));
assertThat(response.getStatus()).isEqualTo(204);
@@ -2150,7 +2292,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/attributes/")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.put(Entity.json(new AccountAttributes(false, 2222, null, null, false, null)
.withUnidentifiedAccessKey(new byte[7])));
@@ -2163,7 +2305,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/me")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.delete();
assertThat(response.getStatus()).isEqualTo(204);
@@ -2178,7 +2320,7 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/me")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.delete();
assertThat(response.getStatus()).isEqualTo(500);
@@ -2300,7 +2442,7 @@ class AccountControllerTest {
assertThat(resources.getJerseyTest()
.target(String.format("/v1/accounts/account/%s", UUID.randomUUID()))
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.X_FORWARDED_FOR, "127.0.0.1")
.head()
.getStatus()).isEqualTo(400);
@@ -2352,7 +2494,7 @@ class AccountControllerTest {
assertThat(resources.getJerseyTest()
.target(String.format("/v1/accounts/username_hash/%s", USERNAME_HASH_1))
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.header(HttpHeaders.X_FORWARDED_FOR, "127.0.0.1")
.get()
.getStatus()).isEqualTo(400);

View File

@@ -12,6 +12,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -20,6 +21,7 @@ import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.uuid.UUIDComparator;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
@@ -32,12 +34,21 @@ import java.util.Random;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import org.apache.commons.lang3.RandomUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
import org.whispersystems.textsecuregcm.push.ClientPresenceManager;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.securebackup.SecureBackupClient;
import org.whispersystems.textsecuregcm.securestorage.SecureStorageClient;
import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2Client;
import org.whispersystems.textsecuregcm.storage.DynamoDbExtensionSchema.Tables;
import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
import org.whispersystems.textsecuregcm.tests.util.DevicesHelper;
@@ -68,6 +79,8 @@ class AccountsTest {
private static final int SCAN_PAGE_SIZE = 1;
private static final AtomicInteger ACCOUNT_COUNTER = new AtomicInteger(1);
@RegisterExtension
static final DynamoDbExtension DYNAMO_DB_EXTENSION = new DynamoDbExtension(
@@ -100,6 +113,88 @@ class AccountsTest {
SCAN_PAGE_SIZE);
}
@Test
public void testStoreAndLookupUsernameLink() throws Exception {
final Account account = nextRandomAccount();
account.setUsernameHash(RandomUtils.nextBytes(16));
accounts.create(account);
final BiConsumer<Optional<Account>, byte[]> validator = (maybeAccount, expectedEncryptedUsername) -> {
assertTrue(maybeAccount.isPresent());
assertTrue(maybeAccount.get().getEncryptedUsername().isPresent());
assertEquals(account.getUuid(), maybeAccount.get().getUuid());
assertArrayEquals(expectedEncryptedUsername, maybeAccount.get().getEncryptedUsername().get());
};
// creating a username link, storing it, checking that it can be looked up
final UUID linkHandle1 = UUID.randomUUID();
final byte[] encruptedUsername1 = RandomUtils.nextBytes(32);
account.setUsernameLinkDetails(linkHandle1, encruptedUsername1);
accounts.update(account);
validator.accept(accounts.getByUsernameLinkHandle(linkHandle1), encruptedUsername1);
// updating username link, storing new one, checking that it can be looked up, checking that old one can't be looked up
final UUID linkHandle2 = UUID.randomUUID();
final byte[] encruptedUsername2 = RandomUtils.nextBytes(32);
account.setUsernameLinkDetails(linkHandle2, encruptedUsername2);
accounts.update(account);
validator.accept(accounts.getByUsernameLinkHandle(linkHandle2), encruptedUsername2);
assertTrue(accounts.getByUsernameLinkHandle(linkHandle1).isEmpty());
// deleting username link, checking it can't be looked up by either handle
account.setUsernameLinkDetails(null, null);
accounts.update(account);
assertTrue(accounts.getByUsernameLinkHandle(linkHandle1).isEmpty());
assertTrue(accounts.getByUsernameLinkHandle(linkHandle2).isEmpty());
}
@Test
public void testUsernameLinksViaAccountsManager() throws Exception {
final AccountsManager accountsManager = new AccountsManager(
accounts,
mock(PhoneNumberIdentifiers.class),
mock(FaultTolerantRedisCluster.class),
mock(DeletedAccountsManager.class),
mock(Keys.class),
mock(MessagesManager.class),
mock(ProfilesManager.class),
mock(StoredVerificationCodeManager.class),
mock(SecureStorageClient.class),
mock(SecureBackupClient.class),
mock(SecureValueRecovery2Client.class),
mock(ClientPresenceManager.class),
mock(ExperimentEnrollmentManager.class),
mock(RegistrationRecoveryPasswordsManager.class),
mock(Clock.class));
final Account account = nextRandomAccount();
account.setUsernameHash(RandomUtils.nextBytes(16));
accounts.create(account);
final UUID linkHandle = UUID.randomUUID();
final byte[] encruptedUsername = RandomUtils.nextBytes(32);
accountsManager.update(account, a -> a.setUsernameLinkDetails(linkHandle, encruptedUsername));
final Optional<Account> maybeAccount = accountsManager.getByUsernameLinkHandle(linkHandle);
assertTrue(maybeAccount.isPresent());
assertTrue(maybeAccount.get().getEncryptedUsername().isPresent());
assertArrayEquals(encruptedUsername, maybeAccount.get().getEncryptedUsername().get());
// making some unrelated change and updating account to check that username link data is still there
final Optional<Account> accountToChange = accountsManager.getByAccountIdentifier(account.getUuid());
assertTrue(accountToChange.isPresent());
accountsManager.update(accountToChange.get(), a -> a.setDiscoverableByPhoneNumber(!a.isDiscoverableByPhoneNumber()));
final Optional<Account> accountAfterChange = accountsManager.getByUsernameLinkHandle(linkHandle);
assertTrue(accountAfterChange.isPresent());
assertTrue(accountAfterChange.get().getEncryptedUsername().isPresent());
assertArrayEquals(encruptedUsername, accountAfterChange.get().getEncryptedUsername().get());
// now deleting the link
final Optional<Account> accountToDeleteLink = accountsManager.getByAccountIdentifier(account.getUuid());
accountsManager.update(accountToDeleteLink.get(), a -> a.setUsernameLinkDetails(null, null));
assertTrue(accounts.getByUsernameLinkHandle(linkHandle).isEmpty());
}
@Test
void testStore() {
Device device = generateDevice(1);
@@ -818,19 +913,24 @@ class AccountsTest {
assertThat(account.getUsernameHash()).isEmpty();
}
private Device generateDevice(long id) {
private static Device generateDevice(long id) {
return DevicesHelper.createDevice(id);
}
private Account generateAccount(String number, UUID uuid, final UUID pni) {
private static Account nextRandomAccount() {
final String nextNumber = "+1800%07d".formatted(ACCOUNT_COUNTER.getAndIncrement());
return generateAccount(nextNumber, UUID.randomUUID(), UUID.randomUUID());
}
private static Account generateAccount(String number, UUID uuid, final UUID pni) {
Device device = generateDevice(1);
return generateAccount(number, uuid, pni, List.of(device));
}
private Account generateAccount(String number, UUID uuid, final UUID pni, List<Device> devices) {
byte[] unidentifiedAccessKey = new byte[16];
Random random = new Random(System.currentTimeMillis());
Arrays.fill(unidentifiedAccessKey, (byte)random.nextInt(255));
private static Account generateAccount(String number, UUID uuid, final UUID pni, List<Device> devices) {
final byte[] unidentifiedAccessKey = new byte[16];
final Random random = new Random(System.currentTimeMillis());
Arrays.fill(unidentifiedAccessKey, (byte) random.nextInt(255));
return AccountsHelper.generateTestAccount(number, uuid, pni, devices, unidentifiedAccessKey);
}

View File

@@ -23,11 +23,29 @@ public final class DynamoDbExtensionSchema {
ACCOUNTS("accounts_test",
Accounts.KEY_ACCOUNT_UUID,
null,
List.of(AttributeDefinition.builder()
.attributeName(Accounts.KEY_ACCOUNT_UUID)
.attributeType(ScalarAttributeType.B)
.build()),
List.of(), List.of()),
List.of(
AttributeDefinition.builder()
.attributeName(Accounts.KEY_ACCOUNT_UUID)
.attributeType(ScalarAttributeType.B)
.build(),
AttributeDefinition.builder()
.attributeName(Accounts.ATTR_USERNAME_LINK_UUID)
.attributeType(ScalarAttributeType.B)
.build()),
List.of(
GlobalSecondaryIndex.builder()
.indexName(Accounts.USERNAME_LINK_TO_UUID_INDEX)
.keySchema(
KeySchemaElement.builder()
.attributeName(Accounts.ATTR_USERNAME_LINK_UUID)
.keyType(KeyType.HASH)
.build()
)
.projection(Projection.builder().projectionType(ProjectionType.KEYS_ONLY).build())
.provisionedThroughput(ProvisionedThroughput.builder().readCapacityUnits(10L).writeCapacityUnits(10L).build())
.build()
),
List.of()),
DELETED_ACCOUNTS("deleted_accounts_test",
DeletedAccounts.KEY_ACCOUNT_E164,

View File

@@ -114,6 +114,7 @@ public class AccountsHelper {
case "getPhoneNumberIdentifier" -> when(updatedAccount.getPhoneNumberIdentifier()).thenAnswer(stubbing);
case "getNumber" -> when(updatedAccount.getNumber()).thenAnswer(stubbing);
case "getUsername" -> when(updatedAccount.getUsernameHash()).thenAnswer(stubbing);
case "getUsernameHash" -> when(updatedAccount.getUsernameHash()).thenAnswer(stubbing);
case "getDevices" -> when(updatedAccount.getDevices()).thenAnswer(stubbing);
case "getDevice" -> when(updatedAccount.getDevice(stubbing.getInvocation().getArgument(0))).thenAnswer(stubbing);
case "getMasterDevice" -> when(updatedAccount.getMasterDevice()).thenAnswer(stubbing);

View File

@@ -11,7 +11,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import java.time.Duration;
import java.util.Optional;
import java.util.UUID;
import org.apache.commons.lang3.RandomUtils;
import org.mockito.Mockito;
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
@@ -50,7 +50,20 @@ public final class MockUtils {
final RateLimiters.For handle,
final String input) {
final RateLimiter mockRateLimiter = Mockito.mock(RateLimiter.class);
doReturn(Optional.of(mockRateLimiter)).when(rateLimitersMock).forDescriptor(eq(handle));
doReturn(mockRateLimiter).when(rateLimitersMock).forDescriptor(eq(handle));
try {
doNothing().when(mockRateLimiter).validate(eq(input));
} catch (final RateLimitExceededException e) {
throw new RuntimeException(e);
}
}
public static void updateRateLimiterResponseToAllow(
final RateLimiters rateLimitersMock,
final RateLimiters.For handle,
final UUID input) {
final RateLimiter mockRateLimiter = Mockito.mock(RateLimiter.class);
doReturn(mockRateLimiter).when(rateLimitersMock).forDescriptor(eq(handle));
try {
doNothing().when(mockRateLimiter).validate(eq(input));
} catch (final RateLimitExceededException e) {
@@ -73,6 +86,21 @@ public final class MockUtils {
}
}
public static void updateRateLimiterResponseToFail(
final RateLimiters rateLimitersMock,
final RateLimiters.For handle,
final UUID input,
final Duration retryAfter,
final boolean legacyStatusCode) {
final RateLimiter mockRateLimiter = Mockito.mock(RateLimiter.class);
doReturn(mockRateLimiter).when(rateLimitersMock).forDescriptor(eq(handle));
try {
doThrow(new RateLimitExceededException(retryAfter, legacyStatusCode)).when(mockRateLimiter).validate(eq(input));
} catch (final RateLimitExceededException e) {
throw new RuntimeException(e);
}
}
public static SecretBytes randomSecretBytes(final int size) {
return new SecretBytes(RandomUtils.nextBytes(size));
}