mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-21 05:58:00 +01:00
Support for versioned profiles
Includes support for issuing zkgroup auth credentials
This commit is contained in:
@@ -4,18 +4,27 @@ import com.google.common.collect.ImmutableSet;
|
||||
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.signal.zkgroup.InvalidInputException;
|
||||
import org.signal.zkgroup.ServerSecretParams;
|
||||
import org.signal.zkgroup.VerificationFailedException;
|
||||
import org.signal.zkgroup.auth.AuthCredential;
|
||||
import org.signal.zkgroup.auth.AuthCredentialResponse;
|
||||
import org.signal.zkgroup.auth.ClientZkAuthOperations;
|
||||
import org.signal.zkgroup.auth.ServerZkAuthOperations;
|
||||
import org.whispersystems.textsecuregcm.auth.CertificateGenerator;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
|
||||
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
|
||||
import org.whispersystems.textsecuregcm.controllers.CertificateController;
|
||||
import org.whispersystems.textsecuregcm.crypto.Curve;
|
||||
import org.whispersystems.textsecuregcm.entities.DeliveryCertificate;
|
||||
import org.whispersystems.textsecuregcm.entities.GroupCredentials;
|
||||
import org.whispersystems.textsecuregcm.entities.MessageProtos.SenderCertificate;
|
||||
import org.whispersystems.textsecuregcm.entities.MessageProtos.ServerCertificate;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
import org.whispersystems.textsecuregcm.util.Base64;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
import org.whispersystems.textsecuregcm.util.Util;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
@@ -24,6 +33,7 @@ import java.util.Arrays;
|
||||
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
|
||||
import io.dropwizard.testing.junit.ResourceTestRule;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.assertj.core.api.Java6Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
@@ -35,11 +45,14 @@ public class CertificateControllerTest {
|
||||
private static final String signingCertificate = "CiUIDBIhBbTz4h1My+tt+vw+TVscgUe/DeHS0W02tPWAWbTO2xc3EkD+go4bJnU0AcnFfbOLKoiBfCzouZtDYMOVi69rE7r4U9cXREEqOkUmU2WJBjykAxWPCcSTmVTYHDw7hkSp/puG";
|
||||
private static final String signingKey = "ABOxG29xrfq4E7IrW11Eg7+HBbtba9iiS0500YoBjn4=";
|
||||
|
||||
private static CertificateGenerator certificateGenerator;
|
||||
private static ServerSecretParams serverSecretParams = ServerSecretParams.generate();
|
||||
private static CertificateGenerator certificateGenerator;
|
||||
private static ServerZkAuthOperations serverZkAuthOperations;
|
||||
|
||||
static {
|
||||
try {
|
||||
certificateGenerator = new CertificateGenerator(Base64.decode(signingCertificate), Curve.decodePrivatePoint(Base64.decode(signingKey)), 1);
|
||||
certificateGenerator = new CertificateGenerator(Base64.decode(signingCertificate), Curve.decodePrivatePoint(Base64.decode(signingKey)), 1);
|
||||
serverZkAuthOperations = new ServerZkAuthOperations(serverSecretParams);
|
||||
} catch (IOException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
@@ -52,7 +65,7 @@ public class CertificateControllerTest {
|
||||
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(ImmutableSet.of(Account.class, DisabledPermittedAccount.class)))
|
||||
.setMapper(SystemMapper.getMapper())
|
||||
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
||||
.addResource(new CertificateController(certificateGenerator))
|
||||
.addResource(new CertificateController(certificateGenerator, serverZkAuthOperations))
|
||||
.build();
|
||||
|
||||
|
||||
@@ -150,5 +163,73 @@ public class CertificateControllerTest {
|
||||
assertEquals(response.getStatus(), 401);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSingleAuthCredential() throws InvalidInputException, VerificationFailedException {
|
||||
GroupCredentials credentials = resources.getJerseyTest()
|
||||
.target("/v1/certificate/group/" + Util.currentDaysSinceEpoch() + "/" + Util.currentDaysSinceEpoch())
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
|
||||
.get(GroupCredentials.class);
|
||||
|
||||
assertThat(credentials.getCredentials().size()).isEqualTo(1);
|
||||
assertThat(credentials.getCredentials().get(0).getRedemptionTime()).isEqualTo(Util.currentDaysSinceEpoch());
|
||||
|
||||
ClientZkAuthOperations clientZkAuthOperations = new ClientZkAuthOperations(serverSecretParams.getPublicParams());
|
||||
AuthCredential credential = clientZkAuthOperations.receiveAuthCredential(AuthHelper.VALID_UUID, Util.currentDaysSinceEpoch(), new AuthCredentialResponse(credentials.getCredentials().get(0).getCredential()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetWeekLongAuthCredentials() throws InvalidInputException, VerificationFailedException {
|
||||
GroupCredentials credentials = resources.getJerseyTest()
|
||||
.target("/v1/certificate/group/" + Util.currentDaysSinceEpoch() + "/" + (Util.currentDaysSinceEpoch() + 7))
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
|
||||
.get(GroupCredentials.class);
|
||||
|
||||
assertThat(credentials.getCredentials().size()).isEqualTo(8);
|
||||
|
||||
for (int i=0;i<=7;i++) {
|
||||
assertThat(credentials.getCredentials().get(i).getRedemptionTime()).isEqualTo(Util.currentDaysSinceEpoch() + i);
|
||||
|
||||
ClientZkAuthOperations clientZkAuthOperations = new ClientZkAuthOperations(serverSecretParams.getPublicParams());
|
||||
AuthCredential credential = clientZkAuthOperations.receiveAuthCredential(AuthHelper.VALID_UUID, Util.currentDaysSinceEpoch() + i , new AuthCredentialResponse(credentials.getCredentials().get(i).getCredential()));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTooManyDaysOut() throws InvalidInputException {
|
||||
Response response = resources.getJerseyTest()
|
||||
.target("/v1/certificate/group/" + Util.currentDaysSinceEpoch() + "/" + (Util.currentDaysSinceEpoch() + 8))
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
|
||||
.get();
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(400);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBackwardsInTime() throws InvalidInputException {
|
||||
Response response = resources.getJerseyTest()
|
||||
.target("/v1/certificate/group/" + (Util.currentDaysSinceEpoch() - 1) + "/" + (Util.currentDaysSinceEpoch() + 7))
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
|
||||
.get();
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(400);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadAuth() throws InvalidInputException {
|
||||
Response response = resources.getJerseyTest()
|
||||
.target("/v1/certificate/group/" + Util.currentDaysSinceEpoch() + "/" + (Util.currentDaysSinceEpoch() + 7))
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.INVALID_PASSWORD))
|
||||
.get();
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(401);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.whispersystems.textsecuregcm.tests.controllers;
|
||||
|
||||
import com.amazonaws.services.s3.AmazonS3;
|
||||
import com.amazonaws.services.s3.AmazonS3Client;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
|
||||
import org.junit.Before;
|
||||
@@ -8,21 +10,32 @@ import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.Mockito;
|
||||
import org.signal.zkgroup.InvalidInputException;
|
||||
import org.signal.zkgroup.profiles.ProfileKey;
|
||||
import org.signal.zkgroup.profiles.ProfileKeyCommitment;
|
||||
import org.signal.zkgroup.profiles.ServerZkProfileOperations;
|
||||
import org.whispersystems.textsecuregcm.auth.AmbiguousIdentifier;
|
||||
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
|
||||
import org.whispersystems.textsecuregcm.configuration.CdnConfiguration;
|
||||
import org.whispersystems.textsecuregcm.controllers.ProfileController;
|
||||
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
|
||||
import org.whispersystems.textsecuregcm.entities.CreateProfileRequest;
|
||||
import org.whispersystems.textsecuregcm.entities.Profile;
|
||||
import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiters;
|
||||
import org.whispersystems.textsecuregcm.s3.PolicySigner;
|
||||
import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.ProfilesManager;
|
||||
import org.whispersystems.textsecuregcm.storage.UsernamesManager;
|
||||
import org.whispersystems.textsecuregcm.storage.VersionedProfile;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
import org.whispersystems.textsecuregcm.util.SystemMapper;
|
||||
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -34,18 +47,17 @@ import static org.mockito.Mockito.*;
|
||||
public class ProfileControllerTest {
|
||||
|
||||
private static AccountsManager accountsManager = mock(AccountsManager.class );
|
||||
private static ProfilesManager profilesManager = mock(ProfilesManager.class);
|
||||
private static UsernamesManager usernamesManager = mock(UsernamesManager.class);
|
||||
private static RateLimiters rateLimiters = mock(RateLimiters.class );
|
||||
private static RateLimiter rateLimiter = mock(RateLimiter.class );
|
||||
private static RateLimiter usernameRateLimiter = mock(RateLimiter.class );
|
||||
private static CdnConfiguration configuration = mock(CdnConfiguration.class);
|
||||
|
||||
static {
|
||||
when(configuration.getAccessKey()).thenReturn("accessKey");
|
||||
when(configuration.getAccessSecret()).thenReturn("accessSecret");
|
||||
when(configuration.getRegion()).thenReturn("us-east-1");
|
||||
when(configuration.getBucket()).thenReturn("profile-bucket");
|
||||
}
|
||||
private static AmazonS3 s3client = mock(AmazonS3.class);
|
||||
private static PostPolicyGenerator postPolicyGenerator = new PostPolicyGenerator("us-west-1", "profile-bucket", "accessKey");
|
||||
private static PolicySigner policySigner = new PolicySigner("accessSecret", "us-west-1");
|
||||
private static ServerZkProfileOperations zkProfileOperations = mock(ServerZkProfileOperations.class);
|
||||
|
||||
|
||||
@ClassRule
|
||||
public static final ResourceTestRule resources = ResourceTestRule.builder()
|
||||
@@ -55,8 +67,13 @@ public class ProfileControllerTest {
|
||||
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
||||
.addResource(new ProfileController(rateLimiters,
|
||||
accountsManager,
|
||||
profilesManager,
|
||||
usernamesManager,
|
||||
configuration))
|
||||
s3client,
|
||||
postPolicyGenerator,
|
||||
policySigner,
|
||||
"profilesBucket",
|
||||
zkProfileOperations))
|
||||
.build();
|
||||
|
||||
@Before
|
||||
@@ -93,7 +110,14 @@ public class ProfileControllerTest {
|
||||
when(accountsManager.get(AuthHelper.VALID_NUMBER)).thenReturn(Optional.of(capabilitiesAccount));
|
||||
when(accountsManager.get(argThat((ArgumentMatcher<AmbiguousIdentifier>) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(AuthHelper.VALID_NUMBER)))).thenReturn(Optional.of(capabilitiesAccount));
|
||||
|
||||
Mockito.clearInvocations(accountsManager);
|
||||
when(profilesManager.get(eq(AuthHelper.VALID_UUID), eq("someversion"))).thenReturn(Optional.empty());
|
||||
when(profilesManager.get(eq(AuthHelper.VALID_UUID_TWO), eq("validversion"))).thenReturn(Optional.of(new VersionedProfile("validversion", "validname", "profiles/validavatar", "validcommitmnet".getBytes())));
|
||||
|
||||
clearInvocations(rateLimiter);
|
||||
clearInvocations(accountsManager);
|
||||
clearInvocations(usernamesManager);
|
||||
clearInvocations(usernameRateLimiter);
|
||||
clearInvocations(profilesManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -111,8 +135,7 @@ public class ProfileControllerTest {
|
||||
|
||||
verify(accountsManager, times(1)).get(argThat((ArgumentMatcher<AmbiguousIdentifier>) identifier -> identifier != null && identifier.hasUuid() && identifier.getUuid().equals(AuthHelper.VALID_UUID_TWO)));
|
||||
verify(usernamesManager, times(1)).get(eq(AuthHelper.VALID_UUID_TWO));
|
||||
verify(rateLimiter, times(2)).validate(eq(AuthHelper.VALID_NUMBER));
|
||||
reset(rateLimiter);
|
||||
verify(rateLimiter, times(1)).validate(eq(AuthHelper.VALID_NUMBER));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -133,7 +156,6 @@ public class ProfileControllerTest {
|
||||
verify(accountsManager, times(1)).get(argThat((ArgumentMatcher<AmbiguousIdentifier>) identifier -> identifier != null && identifier.hasNumber() && identifier.getNumber().equals(AuthHelper.VALID_NUMBER_TWO)));
|
||||
verifyNoMoreInteractions(usernamesManager);
|
||||
verify(rateLimiter, times(1)).validate(eq(AuthHelper.VALID_NUMBER));
|
||||
reset(rateLimiter);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -188,7 +210,6 @@ public class ProfileControllerTest {
|
||||
|
||||
verify(usernamesManager, times(1)).get(eq("n00bkillerzzzzz"));
|
||||
verify(usernameRateLimiter, times(1)).validate(eq(AuthHelper.VALID_UUID.toString()));
|
||||
reset(usernameRateLimiter);
|
||||
}
|
||||
|
||||
|
||||
@@ -215,7 +236,7 @@ public class ProfileControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetProfileName() {
|
||||
public void testSetProfileNameDeprecated() {
|
||||
Response response = resources.getJerseyTest()
|
||||
.target("/v1/profile/name/123456789012345678901234567890123456789012345678901234567890123456789012")
|
||||
.request()
|
||||
@@ -228,7 +249,7 @@ public class ProfileControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetProfileNameExtended() {
|
||||
public void testSetProfileNameExtendedDeprecated() {
|
||||
Response response = resources.getJerseyTest()
|
||||
.target("/v1/profile/name/123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678")
|
||||
.request()
|
||||
@@ -241,7 +262,7 @@ public class ProfileControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetProfileNameWrongSize() {
|
||||
public void testSetProfileNameWrongSizeDeprecated() {
|
||||
Response response = resources.getJerseyTest()
|
||||
.target("/v1/profile/name/1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890")
|
||||
.request()
|
||||
@@ -252,4 +273,113 @@ public class ProfileControllerTest {
|
||||
verifyNoMoreInteractions(accountsManager);
|
||||
}
|
||||
|
||||
/////
|
||||
|
||||
@Test
|
||||
public void testSetProfileWantAvatarUpload() throws InvalidInputException {
|
||||
ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment();
|
||||
|
||||
ProfileAvatarUploadAttributes uploadAttributes = resources.getJerseyTest()
|
||||
.target("/v1/profile/")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
|
||||
.put(Entity.entity(new CreateProfileRequest(commitment, "someversion", "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678", true), MediaType.APPLICATION_JSON_TYPE), ProfileAvatarUploadAttributes.class);
|
||||
|
||||
ArgumentCaptor<VersionedProfile> profileArgumentCaptor = ArgumentCaptor.forClass(VersionedProfile.class);
|
||||
|
||||
verify(profilesManager, times(1)).get(eq(AuthHelper.VALID_UUID), eq("someversion"));
|
||||
verify(profilesManager, times(1)).set(eq(AuthHelper.VALID_UUID), profileArgumentCaptor.capture());
|
||||
|
||||
verifyNoMoreInteractions(s3client);
|
||||
|
||||
assertThat(profileArgumentCaptor.getValue().getCommitment()).isEqualTo(commitment.serialize());
|
||||
assertThat(profileArgumentCaptor.getValue().getAvatar()).isEqualTo(uploadAttributes.getKey());
|
||||
assertThat(profileArgumentCaptor.getValue().getVersion()).isEqualTo("someversion");
|
||||
assertThat(profileArgumentCaptor.getValue().getName()).isEqualTo("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetProfileWantAvatarUploadWithBadProfileSize() throws InvalidInputException {
|
||||
ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment();
|
||||
|
||||
Response response = resources.getJerseyTest()
|
||||
.target("/v1/profile/")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
|
||||
.put(Entity.entity(new CreateProfileRequest(commitment, "someversion", "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890", true), MediaType.APPLICATION_JSON_TYPE));
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(422);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetProfileWithoutAvatarUpload() throws InvalidInputException {
|
||||
ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment();
|
||||
|
||||
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, "anotherversion", "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678", false), MediaType.APPLICATION_JSON_TYPE));
|
||||
|
||||
assertThat(response.getStatus()).isEqualTo(200);
|
||||
assertThat(response.hasEntity()).isFalse();
|
||||
|
||||
ArgumentCaptor<VersionedProfile> profileArgumentCaptor = ArgumentCaptor.forClass(VersionedProfile.class);
|
||||
|
||||
verify(profilesManager, times(1)).get(eq(AuthHelper.VALID_UUID_TWO), eq("anotherversion"));
|
||||
verify(profilesManager, times(1)).set(eq(AuthHelper.VALID_UUID_TWO), profileArgumentCaptor.capture());
|
||||
|
||||
verifyNoMoreInteractions(s3client);
|
||||
|
||||
assertThat(profileArgumentCaptor.getValue().getCommitment()).isEqualTo(commitment.serialize());
|
||||
assertThat(profileArgumentCaptor.getValue().getAvatar()).isNull();
|
||||
assertThat(profileArgumentCaptor.getValue().getVersion()).isEqualTo("anotherversion");
|
||||
assertThat(profileArgumentCaptor.getValue().getName()).isEqualTo("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetProvfileWithAvatarUploadAndPreviousAvatar() throws InvalidInputException {
|
||||
ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment();
|
||||
|
||||
ProfileAvatarUploadAttributes uploadAttributes= resources.getJerseyTest()
|
||||
.target("/v1/profile/")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER_TWO, AuthHelper.VALID_PASSWORD_TWO))
|
||||
.put(Entity.entity(new CreateProfileRequest(commitment, "validversion", "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678", true), MediaType.APPLICATION_JSON_TYPE), ProfileAvatarUploadAttributes.class);
|
||||
|
||||
ArgumentCaptor<VersionedProfile> profileArgumentCaptor = ArgumentCaptor.forClass(VersionedProfile.class);
|
||||
|
||||
verify(profilesManager, times(1)).get(eq(AuthHelper.VALID_UUID_TWO), eq("validversion"));
|
||||
verify(profilesManager, times(1)).set(eq(AuthHelper.VALID_UUID_TWO), profileArgumentCaptor.capture());
|
||||
verify(s3client, times(1)).deleteObject(eq("profilesBucket"), eq("profiles/validavatar"));
|
||||
|
||||
assertThat(profileArgumentCaptor.getValue().getCommitment()).isEqualTo(commitment.serialize());
|
||||
assertThat(profileArgumentCaptor.getValue().getAvatar()).startsWith("profiles/");
|
||||
assertThat(profileArgumentCaptor.getValue().getVersion()).isEqualTo("validversion");
|
||||
assertThat(profileArgumentCaptor.getValue().getName()).isEqualTo("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProfileByVersion() throws RateLimitExceededException {
|
||||
Profile profile = resources.getJerseyTest()
|
||||
.target("/v1/profile/" + AuthHelper.VALID_UUID_TWO + "/validversion")
|
||||
.request()
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
|
||||
.get(Profile.class);
|
||||
|
||||
assertThat(profile.getIdentityKey()).isEqualTo("bar");
|
||||
assertThat(profile.getName()).isEqualTo("validname");
|
||||
assertThat(profile.getAvatar()).isEqualTo("profiles/validavatar");
|
||||
assertThat(profile.getCapabilities().isUuid()).isFalse();
|
||||
assertThat(profile.getUsername()).isEqualTo("n00bkiller");
|
||||
assertThat(profile.getUuid()).isNull();;
|
||||
|
||||
verify(accountsManager, times(1)).get(eq(AuthHelper.VALID_UUID_TWO));
|
||||
verify(usernamesManager, times(1)).get(eq(AuthHelper.VALID_UUID_TWO));
|
||||
verify(profilesManager, times(1)).get(eq(AuthHelper.VALID_UUID_TWO), eq("validversion"));
|
||||
|
||||
verify(rateLimiter, times(1)).validate(eq(AuthHelper.VALID_NUMBER));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package org.whispersystems.textsecuregcm.tests.storage;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.whispersystems.textsecuregcm.redis.ReplicatedJedisPool;
|
||||
import org.whispersystems.textsecuregcm.storage.Profiles;
|
||||
import org.whispersystems.textsecuregcm.storage.ProfilesManager;
|
||||
import org.whispersystems.textsecuregcm.storage.VersionedProfile;
|
||||
import org.whispersystems.textsecuregcm.util.Base64;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static junit.framework.TestCase.assertSame;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.assertj.core.api.Java6Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.exceptions.JedisException;
|
||||
|
||||
public class ProfilesManagerTest {
|
||||
|
||||
@Test
|
||||
public void testGetProfileInCache() {
|
||||
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
|
||||
Jedis jedis = mock(Jedis.class );
|
||||
Profiles profiles = mock(Profiles.class );
|
||||
|
||||
UUID uuid = UUID.randomUUID();
|
||||
|
||||
when(cacheClient.getReadResource()).thenReturn(jedis);
|
||||
when(jedis.hget(eq("profiles::" + uuid.toString()), eq("someversion"))).thenReturn("{\"version\": \"someversion\", \"name\": \"somename\", \"avatar\": \"someavatar\", \"commitment\":\"" + Base64.encodeBytes("somecommitment".getBytes()) + "\"}");
|
||||
|
||||
ProfilesManager profilesManager = new ProfilesManager(profiles, cacheClient);
|
||||
Optional<VersionedProfile> profile = profilesManager.get(uuid, "someversion");
|
||||
|
||||
assertTrue(profile.isPresent());
|
||||
assertEquals(profile.get().getName(), "somename");
|
||||
assertEquals(profile.get().getAvatar(), "someavatar");
|
||||
assertThat(profile.get().getCommitment()).isEqualTo("somecommitment".getBytes());
|
||||
|
||||
verify(jedis, times(1)).hget(eq("profiles::" + uuid.toString()), eq("someversion"));
|
||||
verify(jedis, times(1)).close();
|
||||
verifyNoMoreInteractions(jedis);
|
||||
verifyNoMoreInteractions(profiles);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProfileNotInCache() {
|
||||
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
|
||||
Jedis jedis = mock(Jedis.class );
|
||||
Profiles profiles = mock(Profiles.class );
|
||||
|
||||
UUID uuid = UUID.randomUUID();
|
||||
VersionedProfile profile = new VersionedProfile("someversion", "somename", "someavatar", "somecommitment".getBytes());
|
||||
|
||||
when(cacheClient.getReadResource()).thenReturn(jedis);
|
||||
when(cacheClient.getWriteResource()).thenReturn(jedis);
|
||||
when(jedis.hget(eq("profiles::" + uuid.toString()), eq("someversion"))).thenReturn(null);
|
||||
when(profiles.get(eq(uuid), eq("someversion"))).thenReturn(Optional.of(profile));
|
||||
|
||||
ProfilesManager profilesManager = new ProfilesManager(profiles, cacheClient);
|
||||
Optional<VersionedProfile> retrieved = profilesManager.get(uuid, "someversion");
|
||||
|
||||
assertTrue(retrieved.isPresent());
|
||||
assertSame(retrieved.get(), profile);
|
||||
|
||||
verify(jedis, times(1)).hget(eq("profiles::" + uuid.toString()), eq("someversion"));
|
||||
verify(jedis, times(1)).hset(eq("profiles::" + uuid.toString()), eq("someversion"), anyString());
|
||||
verify(jedis, times(2)).close();
|
||||
verifyNoMoreInteractions(jedis);
|
||||
|
||||
verify(profiles, times(1)).get(eq(uuid), eq("someversion"));
|
||||
verifyNoMoreInteractions(profiles);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProfileBrokenCache() {
|
||||
ReplicatedJedisPool cacheClient = mock(ReplicatedJedisPool.class);
|
||||
Jedis jedis = mock(Jedis.class );
|
||||
Profiles profiles = mock(Profiles.class );
|
||||
|
||||
UUID uuid = UUID.randomUUID();
|
||||
VersionedProfile profile = new VersionedProfile("someversion", "somename", "someavatar", "somecommitment".getBytes());
|
||||
|
||||
when(cacheClient.getReadResource()).thenReturn(jedis);
|
||||
when(cacheClient.getWriteResource()).thenReturn(jedis);
|
||||
when(jedis.hget(eq("profiles::" + uuid.toString()), eq("someversion"))).thenThrow(new JedisException("Connection lost"));
|
||||
when(profiles.get(eq(uuid), eq("someversion"))).thenReturn(Optional.of(profile));
|
||||
|
||||
ProfilesManager profilesManager = new ProfilesManager(profiles, cacheClient);
|
||||
Optional<VersionedProfile> retrieved = profilesManager.get(uuid, "someversion");
|
||||
|
||||
assertTrue(retrieved.isPresent());
|
||||
assertSame(retrieved.get(), profile);
|
||||
|
||||
verify(jedis, times(1)).hget(eq("profiles::" + uuid.toString()), eq("someversion"));
|
||||
verify(jedis, times(1)).hset(eq("profiles::" + uuid.toString()), eq("someversion"), anyString());
|
||||
verify(jedis, times(2)).close();
|
||||
verifyNoMoreInteractions(jedis);
|
||||
|
||||
verify(profiles, times(1)).get(eq(uuid), eq("someversion"));
|
||||
verifyNoMoreInteractions(profiles);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package org.whispersystems.textsecuregcm.tests.storage;
|
||||
|
||||
import com.opentable.db.postgres.embedded.LiquibasePreparer;
|
||||
import com.opentable.db.postgres.junit.EmbeddedPostgresRules;
|
||||
import com.opentable.db.postgres.junit.PreparedDbRule;
|
||||
import org.jdbi.v3.core.Jdbi;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.whispersystems.textsecuregcm.configuration.CircuitBreakerConfiguration;
|
||||
import org.whispersystems.textsecuregcm.storage.FaultTolerantDatabase;
|
||||
import org.whispersystems.textsecuregcm.storage.Profiles;
|
||||
import org.whispersystems.textsecuregcm.storage.VersionedProfile;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
public class ProfilesTest {
|
||||
|
||||
@Rule
|
||||
public PreparedDbRule db = EmbeddedPostgresRules.preparedDatabase(LiquibasePreparer.forClasspathLocation("accountsdb.xml"));
|
||||
|
||||
private Profiles profiles;
|
||||
|
||||
@Before
|
||||
public void setupProfilesDao() {
|
||||
FaultTolerantDatabase faultTolerantDatabase = new FaultTolerantDatabase("profilesTest",
|
||||
Jdbi.create(db.getTestDatabase()),
|
||||
new CircuitBreakerConfiguration());
|
||||
|
||||
this.profiles = new Profiles(faultTolerantDatabase);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetGet() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
VersionedProfile profile = new VersionedProfile("123", "foo", "avatarLocation", "acommitment".getBytes());
|
||||
profiles.set(uuid, profile);
|
||||
|
||||
Optional<VersionedProfile> retrieved = profiles.get(uuid, "123");
|
||||
|
||||
assertThat(retrieved.isPresent()).isTrue();
|
||||
assertThat(retrieved.get().getName()).isEqualTo(profile.getName());
|
||||
assertThat(retrieved.get().getAvatar()).isEqualTo(profile.getAvatar());
|
||||
assertThat(retrieved.get().getCommitment()).isEqualTo(profile.getCommitment());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetReplace() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
VersionedProfile profile = new VersionedProfile("123", "foo", "avatarLocation", "acommitment".getBytes());
|
||||
profiles.set(uuid, profile);
|
||||
|
||||
Optional<VersionedProfile> retrieved = profiles.get(uuid, "123");
|
||||
|
||||
assertThat(retrieved.isPresent()).isTrue();
|
||||
assertThat(retrieved.get().getName()).isEqualTo(profile.getName());
|
||||
assertThat(retrieved.get().getAvatar()).isEqualTo(profile.getAvatar());
|
||||
assertThat(retrieved.get().getCommitment()).isEqualTo(profile.getCommitment());
|
||||
|
||||
VersionedProfile updated = new VersionedProfile("123", "bar", "baz", "boof".getBytes());
|
||||
profiles.set(uuid, updated);
|
||||
|
||||
retrieved = profiles.get(uuid, "123");
|
||||
|
||||
assertThat(retrieved.isPresent()).isTrue();
|
||||
assertThat(retrieved.get().getName()).isEqualTo(updated.getName());
|
||||
assertThat(retrieved.get().getAvatar()).isEqualTo(updated.getAvatar());
|
||||
assertThat(retrieved.get().getCommitment()).isEqualTo(profile.getCommitment());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleVersions() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
VersionedProfile profileOne = new VersionedProfile("123", "foo", "avatarLocation", "acommitmnet".getBytes());
|
||||
VersionedProfile profileTwo = new VersionedProfile("345", "bar", "baz", "boof".getBytes());
|
||||
|
||||
profiles.set(uuid, profileOne);
|
||||
profiles.set(uuid, profileTwo);
|
||||
|
||||
Optional<VersionedProfile> retrieved = profiles.get(uuid, "123");
|
||||
|
||||
assertThat(retrieved.isPresent()).isTrue();
|
||||
assertThat(retrieved.get().getName()).isEqualTo(profileOne.getName());
|
||||
assertThat(retrieved.get().getAvatar()).isEqualTo(profileOne.getAvatar());
|
||||
assertThat(retrieved.get().getCommitment()).isEqualTo(profileOne.getCommitment());
|
||||
|
||||
retrieved = profiles.get(uuid, "345");
|
||||
|
||||
assertThat(retrieved.isPresent()).isTrue();
|
||||
assertThat(retrieved.get().getName()).isEqualTo(profileTwo.getName());
|
||||
assertThat(retrieved.get().getAvatar()).isEqualTo(profileTwo.getAvatar());
|
||||
assertThat(retrieved.get().getCommitment()).isEqualTo(profileTwo.getCommitment());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMissing() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
VersionedProfile profile = new VersionedProfile("123", "foo", "avatarLocation", "aDigest".getBytes());
|
||||
profiles.set(uuid, profile);
|
||||
|
||||
Optional<VersionedProfile> retrieved = profiles.get(uuid, "888");
|
||||
assertThat(retrieved.isPresent()).isFalse();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testDelete() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
VersionedProfile profileOne = new VersionedProfile("123", "foo", "avatarLocation", "aDigest".getBytes());
|
||||
VersionedProfile profileTwo = new VersionedProfile("345", "bar", "baz", "boof".getBytes());
|
||||
|
||||
profiles.set(uuid, profileOne);
|
||||
profiles.set(uuid, profileTwo);
|
||||
|
||||
profiles.deleteAll(uuid);
|
||||
|
||||
Optional<VersionedProfile> retrieved = profiles.get(uuid, "123");
|
||||
|
||||
assertThat(retrieved.isPresent()).isFalse();
|
||||
|
||||
retrieved = profiles.get(uuid, "345");
|
||||
|
||||
assertThat(retrieved.isPresent()).isFalse();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user