Write identity key into 'account' object.
This is the beginning of a migration to storing one identity key per account, instead of the braindead duplication we're doing now. Part one of a two-part deployment in the schema migration process.
This commit is contained in:
parent
f14c181840
commit
437eb8de37
|
@ -7,3 +7,5 @@ run.sh
|
|||
local.yml
|
||||
config/production.yml
|
||||
config/federated.yml
|
||||
config/staging.yml
|
||||
.opsmanage
|
||||
|
|
|
@ -70,7 +70,14 @@ public class KeysController {
|
|||
@PUT
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public void setKeys(@Auth Account account, @Valid PreKeyList preKeys) {
|
||||
Device device = account.getAuthenticatedDevice().get();
|
||||
Device device = account.getAuthenticatedDevice().get();
|
||||
String identityKey = preKeys.getLastResortKey().getIdentityKey();
|
||||
|
||||
if (!identityKey.equals(account.getIdentityKey())) {
|
||||
account.setIdentityKey(identityKey);
|
||||
accounts.update(account);
|
||||
}
|
||||
|
||||
keys.store(account.getNumber(), device.getId(), preKeys.getKeys(), preKeys.getLastResortKey());
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.whispersystems.textsecuregcm.entities;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import org.hibernate.validator.constraints.NotEmpty;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
@ -27,6 +28,7 @@ public class PreKeyList {
|
|||
|
||||
@JsonProperty
|
||||
@NotNull
|
||||
@Valid
|
||||
private PreKey lastResortKey;
|
||||
|
||||
@JsonProperty
|
||||
|
@ -38,7 +40,17 @@ public class PreKeyList {
|
|||
return keys;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setKeys(List<PreKey> keys) {
|
||||
this.keys = keys;
|
||||
}
|
||||
|
||||
public PreKey getLastResortKey() {
|
||||
return lastResortKey;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public void setLastResortKey(PreKey lastResortKey) {
|
||||
this.lastResortKey = lastResortKey;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ import java.util.List;
|
|||
|
||||
public class Account implements Serializable {
|
||||
|
||||
public static final int MEMCACHE_VERION = 2;
|
||||
public static final int MEMCACHE_VERION = 3;
|
||||
|
||||
@JsonIgnore
|
||||
private long id;
|
||||
|
@ -42,16 +42,14 @@ public class Account implements Serializable {
|
|||
@JsonProperty
|
||||
private List<Device> devices = new LinkedList<>();
|
||||
|
||||
@JsonProperty
|
||||
private String identityKey;
|
||||
|
||||
@JsonIgnore
|
||||
private Optional<Device> authenticatedDevice;
|
||||
|
||||
public Account() {}
|
||||
|
||||
public Account(String number, boolean supportsSms) {
|
||||
this.number = number;
|
||||
this.supportsSms = supportsSms;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Account(String number, boolean supportsSms, List<Device> devices) {
|
||||
this.number = number;
|
||||
|
@ -142,4 +140,12 @@ public class Account implements Serializable {
|
|||
public Optional<String> getRelay() {
|
||||
return Optional.absent();
|
||||
}
|
||||
|
||||
public void setIdentityKey(String identityKey) {
|
||||
this.identityKey = identityKey;
|
||||
}
|
||||
|
||||
public String getIdentityKey() {
|
||||
return identityKey;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,10 @@ import com.google.common.base.Optional;
|
|||
import com.sun.jersey.api.client.ClientResponse;
|
||||
import com.yammer.dropwizard.testing.ResourceTest;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.whispersystems.textsecuregcm.controllers.KeysController;
|
||||
import org.whispersystems.textsecuregcm.entities.PreKey;
|
||||
import org.whispersystems.textsecuregcm.entities.PreKeyList;
|
||||
import org.whispersystems.textsecuregcm.entities.PreKeyStatus;
|
||||
import org.whispersystems.textsecuregcm.entities.UnstructuredPreKeyList;
|
||||
import org.whispersystems.textsecuregcm.limits.RateLimiter;
|
||||
|
@ -16,6 +18,7 @@ import org.whispersystems.textsecuregcm.storage.Device;
|
|||
import org.whispersystems.textsecuregcm.storage.Keys;
|
||||
import org.whispersystems.textsecuregcm.tests.util.AuthHelper;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
@ -30,11 +33,13 @@ public class KeyControllerTest extends ResourceTest {
|
|||
private final int SAMPLE_REGISTRATION_ID = 999;
|
||||
private final int SAMPLE_REGISTRATION_ID2 = 1002;
|
||||
|
||||
private final PreKey SAMPLE_KEY = new PreKey(1, EXISTS_NUMBER, Device.MASTER_ID, 1234, "test1", "test2", false);
|
||||
private final PreKey SAMPLE_KEY2 = new PreKey(2, EXISTS_NUMBER, 2, 5667, "test3", "test4", false );
|
||||
private final PreKey SAMPLE_KEY3 = new PreKey(3, EXISTS_NUMBER, 3, 334, "test5", "test6", false);
|
||||
private final Keys keys = mock(Keys.class );
|
||||
private final AccountsManager accounts = mock(AccountsManager.class);
|
||||
private final PreKey SAMPLE_KEY = new PreKey(1, EXISTS_NUMBER, Device.MASTER_ID, 1234, "test1", "test2", false);
|
||||
private final PreKey SAMPLE_KEY2 = new PreKey(2, EXISTS_NUMBER, 2, 5667, "test3", "test4", false );
|
||||
private final PreKey SAMPLE_KEY3 = new PreKey(3, EXISTS_NUMBER, 3, 334, "test5", "test6", false );
|
||||
private final Keys keys = mock(Keys.class );
|
||||
private final AccountsManager accounts = mock(AccountsManager.class);
|
||||
private final Account existsAccount = mock(Account.class );
|
||||
|
||||
|
||||
@Override
|
||||
protected void setUpResources() {
|
||||
|
@ -46,7 +51,6 @@ public class KeyControllerTest extends ResourceTest {
|
|||
Device sampleDevice = mock(Device.class );
|
||||
Device sampleDevice2 = mock(Device.class);
|
||||
Device sampleDevice3 = mock(Device.class);
|
||||
Account existsAccount = mock(Account.class);
|
||||
|
||||
when(sampleDevice.getRegistrationId()).thenReturn(SAMPLE_REGISTRATION_ID);
|
||||
when(sampleDevice2.getRegistrationId()).thenReturn(SAMPLE_REGISTRATION_ID2);
|
||||
|
@ -76,6 +80,8 @@ public class KeyControllerTest extends ResourceTest {
|
|||
when(keys.get(EXISTS_NUMBER)).thenReturn(Optional.of(new UnstructuredPreKeyList(allKeys)));
|
||||
when(keys.getCount(eq(AuthHelper.VALID_NUMBER), eq(1L))).thenReturn(5);
|
||||
|
||||
when(AuthHelper.VALID_ACCOUNT.getIdentityKey()).thenReturn(null);
|
||||
|
||||
addResource(new KeysController(rateLimiters, keys, accounts, null));
|
||||
}
|
||||
|
||||
|
@ -165,4 +171,42 @@ public class KeyControllerTest extends ResourceTest {
|
|||
assertThat(response.getClientResponseStatus().getStatusCode()).isEqualTo(401);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void putKeysTest() throws Exception {
|
||||
final PreKey newKey = new PreKey(0, null, 1L, 31337, "foobar", "foobarbaz", false);
|
||||
final PreKey lastResortKey = new PreKey(0, null, 1L, 0xFFFFFF, "fooz", "foobarbaz", false);
|
||||
|
||||
List<PreKey> preKeys = new LinkedList<PreKey>() {{
|
||||
add(newKey);
|
||||
}};
|
||||
|
||||
PreKeyList preKeyList = new PreKeyList();
|
||||
preKeyList.setKeys(preKeys);
|
||||
preKeyList.setLastResortKey(lastResortKey);
|
||||
|
||||
ClientResponse response =
|
||||
client().resource("/v1/keys")
|
||||
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_NUMBER, AuthHelper.VALID_PASSWORD))
|
||||
.type(MediaType.APPLICATION_JSON_TYPE)
|
||||
.put(ClientResponse.class, preKeyList);
|
||||
|
||||
assertThat(response.getClientResponseStatus().getStatusCode()).isEqualTo(204);
|
||||
|
||||
ArgumentCaptor<List> listCaptor = ArgumentCaptor.forClass(List.class );
|
||||
ArgumentCaptor<PreKey> lastResortCaptor = ArgumentCaptor.forClass(PreKey.class);
|
||||
verify(keys).store(eq(AuthHelper.VALID_NUMBER), eq(1L), listCaptor.capture(), lastResortCaptor.capture());
|
||||
|
||||
List<PreKey> capturedList = listCaptor.getValue();
|
||||
assertThat(capturedList.size() == 1);
|
||||
assertThat(capturedList.get(0).getIdentityKey().equals("foobarbaz"));
|
||||
assertThat(capturedList.get(0).getKeyId() == 31337);
|
||||
assertThat(capturedList.get(0).getPublicKey().equals("foobar"));
|
||||
|
||||
assertThat(lastResortCaptor.getValue().getPublicKey().equals("fooz"));
|
||||
assertThat(lastResortCaptor.getValue().getIdentityKey().equals("foobarbaz"));
|
||||
|
||||
verify(AuthHelper.VALID_ACCOUNT).setIdentityKey(eq("foobarbaz"));
|
||||
verify(accounts).update(AuthHelper.VALID_ACCOUNT);
|
||||
}
|
||||
|
||||
}
|
|
@ -27,20 +27,20 @@ public class AuthHelper {
|
|||
public static final String INVVALID_NUMBER = "+14151111111";
|
||||
public static final String INVALID_PASSWORD = "bar";
|
||||
|
||||
public static MultiBasicAuthProvider<FederatedPeer, Account> getAuthenticator() {
|
||||
AccountsManager accounts = mock(AccountsManager.class );
|
||||
Account account = mock(Account.class );
|
||||
Device device = mock(Device.class );
|
||||
AuthenticationCredentials credentials = mock(AuthenticationCredentials.class);
|
||||
public static AccountsManager ACCOUNTS_MANAGER = mock(AccountsManager.class );
|
||||
public static Account VALID_ACCOUNT = mock(Account.class );
|
||||
public static Device VALID_DEVICE = mock(Device.class );
|
||||
public static AuthenticationCredentials VALID_CREDENTIALS = mock(AuthenticationCredentials.class);
|
||||
|
||||
when(credentials.verify("foo")).thenReturn(true);
|
||||
when(device.getAuthenticationCredentials()).thenReturn(credentials);
|
||||
when(device.getId()).thenReturn(1L);
|
||||
when(account.getDevice(anyLong())).thenReturn(Optional.of(device));
|
||||
when(account.getNumber()).thenReturn(VALID_NUMBER);
|
||||
when(account.getAuthenticatedDevice()).thenReturn(Optional.of(device));
|
||||
when(account.getRelay()).thenReturn(Optional.<String>absent());
|
||||
when(accounts.get(VALID_NUMBER)).thenReturn(Optional.of(account));
|
||||
public static MultiBasicAuthProvider<FederatedPeer, Account> getAuthenticator() {
|
||||
when(VALID_CREDENTIALS.verify("foo")).thenReturn(true);
|
||||
when(VALID_DEVICE.getAuthenticationCredentials()).thenReturn(VALID_CREDENTIALS);
|
||||
when(VALID_DEVICE.getId()).thenReturn(1L);
|
||||
when(VALID_ACCOUNT.getDevice(anyLong())).thenReturn(Optional.of(VALID_DEVICE));
|
||||
when(VALID_ACCOUNT.getNumber()).thenReturn(VALID_NUMBER);
|
||||
when(VALID_ACCOUNT.getAuthenticatedDevice()).thenReturn(Optional.of(VALID_DEVICE));
|
||||
when(VALID_ACCOUNT.getRelay()).thenReturn(Optional.<String>absent());
|
||||
when(ACCOUNTS_MANAGER.get(VALID_NUMBER)).thenReturn(Optional.of(VALID_ACCOUNT));
|
||||
|
||||
List<FederatedPeer> peer = new LinkedList<FederatedPeer>() {{
|
||||
add(new FederatedPeer("cyanogen", "https://foo", "foofoo", "bazzzzz"));
|
||||
|
@ -51,7 +51,7 @@ public class AuthHelper {
|
|||
|
||||
return new MultiBasicAuthProvider<>(new FederatedPeerAuthenticator(federationConfiguration),
|
||||
FederatedPeer.class,
|
||||
new AccountAuthenticator(accounts),
|
||||
new AccountAuthenticator(ACCOUNTS_MANAGER),
|
||||
Account.class, "WhisperServer");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue