Skip to content

Commit

Permalink
Add versioned profiles feature flag.
Browse files Browse the repository at this point in the history
  • Loading branch information
alan-signal authored and greyson-signal committed May 29, 2020
1 parent 28bd245 commit 289f7ab
Show file tree
Hide file tree
Showing 11 changed files with 67 additions and 48 deletions.
Expand Up @@ -53,7 +53,7 @@ protected void onRun() throws Exception {
String avatarPath = null;

try (StreamDetails avatar = AvatarHelper.getSelfProfileAvatarStream(context)) {
if (FeatureFlags.VERSIONED_PROFILES) {
if (FeatureFlags.versionedProfiles()) {
avatarPath = accountManager.setVersionedProfile(Recipient.self().getUuid().get(), profileKey, profileName.serialize(), avatar).orNull();
} else {
accountManager.setProfileName(profileKey, profileName.serialize());
Expand Down
Expand Up @@ -93,7 +93,7 @@ private void setProfileKeyCredential(@NonNull Recipient recipient,
}

private static SignalServiceProfile.RequestType getRequestType(@NonNull Recipient recipient) {
return FeatureFlags.VERSIONED_PROFILES && !recipient.hasProfileKeyCredential()
return FeatureFlags.versionedProfiles() && !recipient.hasProfileKeyCredential()
? SignalServiceProfile.RequestType.PROFILE_AND_CREDENTIAL
: SignalServiceProfile.RequestType.PROFILE;
}
Expand Down
Expand Up @@ -145,7 +145,7 @@ private void setProfileKeyCredential(@NonNull Recipient recipient,
}

private static SignalServiceProfile.RequestType getRequestType(@NonNull Recipient recipient) {
return FeatureFlags.VERSIONED_PROFILES && !recipient.hasProfileKeyCredential()
return FeatureFlags.versionedProfiles() && !recipient.hasProfileKeyCredential()
? SignalServiceProfile.RequestType.PROFILE_AND_CREDENTIAL
: SignalServiceProfile.RequestType.PROFILE;
}
Expand Down
Expand Up @@ -57,7 +57,7 @@ public void onRun() throws Exception {
recipientDatabase.setProfileKey(self.getId(), profileKey);

try (StreamDetails avatarStream = AvatarHelper.getSelfProfileAvatarStream(context)) {
if (FeatureFlags.VERSIONED_PROFILES) {
if (FeatureFlags.versionedProfiles()) {
accountManager.setVersionedProfile(self.getUuid().get(),
profileKey,
Recipient.self().getProfileName().serialize(),
Expand Down
Expand Up @@ -11,6 +11,7 @@
import org.json.JSONException;
import org.json.JSONObject;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.jobs.ProfileUploadJob;
import org.thoughtcrime.securesms.jobs.RefreshAttributesJob;
import org.thoughtcrime.securesms.jobs.RemoteConfigRefreshJob;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
Expand Down Expand Up @@ -59,8 +60,9 @@ public final class FeatureFlags {
private static final String REMOTE_DELETE = "android.remoteDelete";
private static final String PROFILE_FOR_CALLING = "android.profileForCalling";
private static final String CALLING_PIP = "android.callingPip";
private static final String NEW_GROUP_UI = "android.newGroupUI";
private static final String REACT_WITH_ANY_EMOJI = "android.reactWithAnyEmoji";
private static final String NEW_GROUP_UI = "android.newGroupUI";
private static final String VERSIONED_PROFILES = "android.versionedProfiles";
private static final String GROUPS_V2 = "android.groupsv2";
private static final String GROUPS_V2_CREATE = "android.groupsv2.create";

Expand All @@ -81,7 +83,8 @@ public final class FeatureFlags {
PROFILE_FOR_CALLING,
CALLING_PIP,
NEW_GROUP_UI,
REACT_WITH_ANY_EMOJI
REACT_WITH_ANY_EMOJI,
VERSIONED_PROFILES
);

/**
Expand Down Expand Up @@ -113,6 +116,7 @@ public final class FeatureFlags {
private static final Set<String> STICKY = Sets.newHashSet(
PINS_FOR_ALL_LEGACY,
PINS_FOR_ALL,
VERSIONED_PROFILES,
GROUPS_V2
);

Expand All @@ -128,8 +132,9 @@ public final class FeatureFlags {
* desired test state.
*/
private static final Map<String, OnFlagChange> FLAG_CHANGE_LISTENERS = new HashMap<String, OnFlagChange>() {{
put(MESSAGE_REQUESTS, (change) -> SignalStore.setMessageRequestEnableTime(change == Change.ENABLED ? System.currentTimeMillis() : 0));
put(GROUPS_V2, (change) -> ApplicationDependencies.getJobManager().add(new RefreshAttributesJob()));
put(MESSAGE_REQUESTS, (change) -> SignalStore.setMessageRequestEnableTime(change == Change.ENABLED ? System.currentTimeMillis() : 0));
put(VERSIONED_PROFILES, (change) -> ApplicationDependencies.getJobManager().add(new ProfileUploadJob()));
put(GROUPS_V2, (change) -> ApplicationDependencies.getJobManager().add(new RefreshAttributesJob()));
}};

private static final Map<String, Object> REMOTE_VALUES = new TreeMap<>();
Expand Down Expand Up @@ -260,9 +265,14 @@ public static boolean reactWithAnyEmoji() {
return getBoolean(REACT_WITH_ANY_EMOJI, false);
}

/** Read and write versioned profile information. */
public static boolean versionedProfiles() {
return getBoolean(VERSIONED_PROFILES, false);
}

/** Groups v2 send and receive. */
public static boolean groupsV2() {
return getBoolean(GROUPS_V2, false);
return versionedProfiles() && getBoolean(GROUPS_V2, false);
}

/** Groups v2 send and receive. */
Expand Down Expand Up @@ -504,7 +514,4 @@ interface OnFlagChange {
enum Change {
ENABLED, DISABLED, CHANGED, REMOVED
}

/** Read and write versioned profile information. */
public static final boolean VERSIONED_PROFILES = org.whispersystems.signalservice.FeatureFlags.VERSIONED_PROFILES;
}
Expand Up @@ -6,6 +6,6 @@
*/
public final class FeatureFlags {

/** Read and write versioned profile information. */
public static final boolean VERSIONED_PROFILES = false;
/** Prevent usage of non-versioned profile endpoints. */
public static final boolean DISALLOW_OLD_PROFILE_SETTING = false;
}
Expand Up @@ -648,7 +648,7 @@ public TurnServerInfo getTurnServerInfo() throws IOException {
public void setProfileName(ProfileKey key, String name)
throws IOException
{
if (FeatureFlags.VERSIONED_PROFILES) {
if (FeatureFlags.DISALLOW_OLD_PROFILE_SETTING) {
throw new AssertionError();
}

Expand All @@ -662,7 +662,7 @@ public void setProfileName(ProfileKey key, String name)
public Optional<String> setProfileAvatar(ProfileKey key, StreamDetails avatar)
throws IOException
{
if (FeatureFlags.VERSIONED_PROFILES) {
if (FeatureFlags.DISALLOW_OLD_PROFILE_SETTING) {
throw new AssertionError();
}

Expand All @@ -684,10 +684,6 @@ public Optional<String> setProfileAvatar(ProfileKey key, StreamDetails avatar)
public Optional<String> setVersionedProfile(UUID uuid, ProfileKey profileKey, String name, StreamDetails avatar)
throws IOException
{
if (!FeatureFlags.VERSIONED_PROFILES) {
throw new AssertionError();
}

if (name == null) name = "";

byte[] ciphertextName = new ProfileCipher(profileKey).encryptName(name.getBytes(StandardCharsets.UTF_8), ProfileCipher.NAME_PADDED_LENGTH);
Expand All @@ -711,7 +707,7 @@ public Optional<String> setVersionedProfile(UUID uuid, ProfileKey profileKey, St
public Optional<ProfileKeyCredential> resolveProfileKeyCredential(UUID uuid, ProfileKey profileKey)
throws NonSuccessfulResponseCodeException, PushNetworkException, VerificationFailedException
{
return this.pushServiceSocket.retrieveProfile(uuid, profileKey, Optional.absent()).getProfileKeyCredential();
return this.pushServiceSocket.retrieveVersionedProfileAndCredential(uuid, profileKey, Optional.absent()).getProfileKeyCredential();
}

public void setUsername(String username) throws IOException {
Expand Down
Expand Up @@ -19,7 +19,6 @@
import org.whispersystems.libsignal.util.Hex;
import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.FeatureFlags;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
import org.whispersystems.signalservice.api.profiles.ProfileAndCredential;
Expand Down Expand Up @@ -187,16 +186,21 @@ public ProfileAndCredential getProfile(SignalServiceAddress address,
.setVerb("GET")
.addAllHeaders(headers);

if (FeatureFlags.VERSIONED_PROFILES && requestType == SignalServiceProfile.RequestType.PROFILE_AND_CREDENTIAL && uuid.isPresent() && profileKey.isPresent()) {
UUID target = uuid.get();
ProfileKeyVersion profileKeyIdentifier = profileKey.get().getProfileKeyVersion(target);
requestContext = clientZkProfile.createProfileKeyCredentialRequestContext(random, target, profileKey.get());
ProfileKeyCredentialRequest request = requestContext.getRequest();
if (uuid.isPresent() && profileKey.isPresent()) {
UUID target = uuid.get();
ProfileKeyVersion profileKeyIdentifier = profileKey.get().getProfileKeyVersion(target);
String version = profileKeyIdentifier.serialize();

String version = profileKeyIdentifier.serialize();
String credentialRequest = Hex.toStringCondensed(request.serialize());
if (requestType == SignalServiceProfile.RequestType.PROFILE_AND_CREDENTIAL) {
requestContext = clientZkProfile.createProfileKeyCredentialRequestContext(random, target, profileKey.get());

builder.setPath(String.format("/v1/profile/%s/%s/%s", target, version, credentialRequest));
ProfileKeyCredentialRequest request = requestContext.getRequest();
String credentialRequest = Hex.toStringCondensed(request.serialize());

builder.setPath(String.format("/v1/profile/%s/%s/%s", target, version, credentialRequest));
} else {
builder.setPath(String.format("/v1/profile/%s/%s", target, version));
}
} else {
builder.setPath(String.format("/v1/profile/%s", address.getIdentifier()));
}
Expand Down
Expand Up @@ -9,10 +9,8 @@
import org.signal.zkgroup.VerificationFailedException;
import org.signal.zkgroup.profiles.ClientZkProfileOperations;
import org.signal.zkgroup.profiles.ProfileKey;
import org.signal.zkgroup.profiles.ProfileKeyCredential;
import org.whispersystems.libsignal.InvalidMessageException;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.FeatureFlags;
import org.whispersystems.signalservice.api.crypto.AttachmentCipherInputStream;
import org.whispersystems.signalservice.api.crypto.ProfileCipherInputStream;
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
Expand Down Expand Up @@ -130,12 +128,18 @@ public ProfileAndCredential retrieveProfile(SignalServiceAddress address,
{
Optional<UUID> uuid = address.getUuid();

if (FeatureFlags.VERSIONED_PROFILES && requestType == SignalServiceProfile.RequestType.PROFILE_AND_CREDENTIAL && uuid.isPresent() && profileKey.isPresent()) {
return socket.retrieveProfile(uuid.get(), profileKey.get(), unidentifiedAccess);
if (uuid.isPresent() && profileKey.isPresent()) {
if (requestType == SignalServiceProfile.RequestType.PROFILE_AND_CREDENTIAL) {
return socket.retrieveVersionedProfileAndCredential(uuid.get(), profileKey.get(), unidentifiedAccess);
} else {
return new ProfileAndCredential(socket.retrieveVersionedProfile(uuid.get(), profileKey.get(), unidentifiedAccess),
SignalServiceProfile.RequestType.PROFILE,
Optional.absent());
}
} else {
return new ProfileAndCredential(socket.retrieveProfile(address, unidentifiedAccess),
SignalServiceProfile.RequestType.PROFILE,
Optional.<ProfileKeyCredential>absent());
Optional.absent());
}
}

Expand Down
Expand Up @@ -131,8 +131,6 @@ public boolean isStorage() {
}

public ProfileKeyCredentialResponse getProfileKeyCredentialResponse() {
if (!FeatureFlags.VERSIONED_PROFILES) return null;

if (credential == null) return null;

try {
Expand Down
Expand Up @@ -595,13 +595,9 @@ public SignalServiceProfile retrieveProfileByUsername(String username, Optional<
}
}

public ProfileAndCredential retrieveProfile(UUID target, ProfileKey profileKey, Optional<UnidentifiedAccess> unidentifiedAccess)
public ProfileAndCredential retrieveVersionedProfileAndCredential(UUID target, ProfileKey profileKey, Optional<UnidentifiedAccess> unidentifiedAccess)
throws NonSuccessfulResponseCodeException, PushNetworkException, VerificationFailedException
{
if (!FeatureFlags.VERSIONED_PROFILES) {
throw new AssertionError();
}

ProfileKeyVersion profileKeyIdentifier = profileKey.getProfileKeyVersion(target);
ProfileKeyCredentialRequestContext requestContext = clientZkProfileOperations.createProfileKeyCredentialRequestContext(random, target, profileKey);
ProfileKeyCredentialRequest request = requestContext.getRequest();
Expand All @@ -626,6 +622,24 @@ public ProfileAndCredential retrieveProfile(UUID target, ProfileKey profileKey,
}
}

public SignalServiceProfile retrieveVersionedProfile(UUID target, ProfileKey profileKey, Optional<UnidentifiedAccess> unidentifiedAccess)
throws NonSuccessfulResponseCodeException, PushNetworkException
{
ProfileKeyVersion profileKeyIdentifier = profileKey.getProfileKeyVersion(target);

String version = profileKeyIdentifier.serialize();
String subPath = String.format("%s/%s", target, version);

String response = makeServiceRequest(String.format(PROFILE_PATH, subPath), "GET", null, NO_HEADERS, unidentifiedAccess);

try {
return JsonUtil.fromJson(response, SignalServiceProfile.class);
} catch (IOException e) {
Log.w(TAG, e);
throw new NonSuccessfulResponseCodeException("Unable to parse entity");
}
}

public void retrieveProfileAvatar(String path, File destination, long maxSizeBytes)
throws NonSuccessfulResponseCodeException, PushNetworkException {
try {
Expand All @@ -636,7 +650,7 @@ public void retrieveProfileAvatar(String path, File destination, long maxSizeByt
}

public void setProfileName(String name) throws NonSuccessfulResponseCodeException, PushNetworkException {
if (FeatureFlags.VERSIONED_PROFILES) {
if (FeatureFlags.DISALLOW_OLD_PROFILE_SETTING) {
throw new AssertionError();
}

Expand All @@ -646,7 +660,7 @@ public void setProfileName(String name) throws NonSuccessfulResponseCodeExceptio
public Optional<String> setProfileAvatar(ProfileAvatarData profileAvatar)
throws NonSuccessfulResponseCodeException, PushNetworkException
{
if (FeatureFlags.VERSIONED_PROFILES) {
if (FeatureFlags.DISALLOW_OLD_PROFILE_SETTING) {
throw new AssertionError();
}

Expand Down Expand Up @@ -680,10 +694,6 @@ public Optional<String> setProfileAvatar(ProfileAvatarData profileAvatar)
public Optional<String> writeProfile(SignalServiceProfileWrite signalServiceProfileWrite, ProfileAvatarData profileAvatar)
throws NonSuccessfulResponseCodeException, PushNetworkException
{
if (!FeatureFlags.VERSIONED_PROFILES) {
throw new AssertionError();
}

String requestBody = JsonUtil.toJson(signalServiceProfileWrite);
ProfileAvatarUploadAttributes formAttributes;

Expand Down

0 comments on commit 289f7ab

Please sign in to comment.