diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt index 9ce35e6a7a..0c837043e3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/app/internal/InternalSettingsFragment.kt @@ -55,18 +55,6 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter private fun getConfiguration(state: InternalSettingsState): DSLConfiguration { return configure { - sectionHeaderPref(R.string.preferences__internal_payments) - - clickPref( - title = DSLSettingsText.from(R.string.preferences__internal_payment_copy_data), - summary = DSLSettingsText.from(R.string.preferences__internal_payment_copy_data_description), - onClick = { - copyPaymentsDataToClipboard() - } - ) - - dividerPref() - sectionHeaderPref(R.string.preferences__internal_account) clickPref( @@ -77,6 +65,14 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter } ) + clickPref( + title = DSLSettingsText.from(R.string.preferences__internal_refresh_profile), + summary = DSLSettingsText.from(R.string.preferences__internal_refresh_profile_description), + onClick = { + refreshProfile() + } + ) + clickPref( title = DSLSettingsText.from(R.string.preferences__internal_rotate_profile_key), summary = DSLSettingsText.from(R.string.preferences__internal_rotate_profile_key_description), @@ -117,6 +113,18 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter dividerPref() + sectionHeaderPref(R.string.preferences__internal_payments) + + clickPref( + title = DSLSettingsText.from(R.string.preferences__internal_payment_copy_data), + summary = DSLSettingsText.from(R.string.preferences__internal_payment_copy_data_description), + onClick = { + copyPaymentsDataToClipboard() + } + ) + + dividerPref() + sectionHeaderPref(R.string.preferences__internal_storage_service) switchPref( @@ -444,6 +452,11 @@ class InternalSettingsFragment : DSLSettingsFragment(R.string.preferences__inter Toast.makeText(context, "Scheduled attribute refresh", Toast.LENGTH_SHORT).show() } + private fun refreshProfile() { + ApplicationDependencies.getJobManager().add(RefreshOwnProfileJob()) + Toast.makeText(context, "Scheduled profile refresh", Toast.LENGTH_SHORT).show() + } + private fun rotateProfileKey() { ApplicationDependencies.getJobManager().add(RotateProfileKeyJob()) Toast.makeText(context, "Scheduled profile key rotation", Toast.LENGTH_SHORT).show() diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java index 8967b19633..e7fb34cb89 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RefreshOwnProfileJob.java @@ -23,9 +23,12 @@ import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.profiles.ProfileName; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.subscription.Subscriber; +import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.ProfileUtil; +import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException; +import org.whispersystems.signalservice.api.crypto.ProfileCipher; import org.whispersystems.signalservice.api.profiles.ProfileAndCredential; import org.whispersystems.signalservice.api.profiles.SignalServiceProfile; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; @@ -117,6 +120,8 @@ public class RefreshOwnProfileJob extends BaseJob { setProfileAvatar(profile.getAvatar()); setProfileCapabilities(profile.getCapabilities()); setProfileBadges(profile.getBadges()); + ensureUnidentifiedAccessCorrect(profile.getUnidentifiedAccess(), profile.isUnrestrictedUnidentifiedAccess()); + Optional profileKeyCredential = profileAndCredential.getProfileKeyCredential(); if (profileKeyCredential.isPresent()) { setProfileKeyCredential(self, ProfileKeyUtil.getSelfProfileKey(), profileKeyCredential.get()); @@ -186,6 +191,36 @@ public class RefreshOwnProfileJob extends BaseJob { SignalDatabase.recipients().setCapabilities(Recipient.self().getId(), capabilities); } + private void ensureUnidentifiedAccessCorrect(@Nullable String unidentifiedAccessVerifier, boolean universalUnidentifiedAccess) { + if (unidentifiedAccessVerifier == null) { + Log.w(TAG, "No unidentified access is set remotely! Refreshing attributes."); + ApplicationDependencies.getJobManager().add(new RefreshAttributesJob()); + return; + } + + if (TextSecurePreferences.isUniversalUnidentifiedAccess(context) != universalUnidentifiedAccess) { + Log.w(TAG, "The universal access flag doesn't match our local value (local: " + TextSecurePreferences.isUniversalUnidentifiedAccess(context) + ", remote: " + universalUnidentifiedAccess + ")! Refreshing attributes."); + ApplicationDependencies.getJobManager().add(new RefreshAttributesJob()); + return; + } + + ProfileKey profileKey = ProfileKeyUtil.getSelfProfileKey(); + ProfileCipher cipher = new ProfileCipher(profileKey); + + boolean verified; + try { + verified = cipher.verifyUnidentifiedAccess(Base64.decode(unidentifiedAccessVerifier)); + } catch (IOException e) { + Log.w(TAG, "Failed to decode unidentified access!", e); + verified = false; + } + + if (!verified) { + Log.w(TAG, "Unidentified access failed to verify! Refreshing attributes."); + ApplicationDependencies.getJobManager().add(new RefreshAttributesJob()); + } + } + private void setProfileBadges(@Nullable List badges) { if (badges == null) { return; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c211b33675..937d28adeb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -2641,6 +2641,8 @@ Account Refresh attributes Forces a write of capabilities on to the server followed by a read. + Refresh profile + Forces a refresh of your own profile. Rotate profile key Creates a new versioned profile, and triggers an update of any GV2 group you belong to. Refresh remote config