diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java index c9b2a3b39..2f74a4015 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java @@ -216,7 +216,7 @@ public class ProfileController { .orElseGet(a::getBadges); a.setBadges(clock, updatedBadges); - a.setCurrentProfileVersion(request.version()); + a.setCurrentProfileVersion(HexFormat.of().parseHex(request.version())); }); if (request.getAvatarChange() == CreateProfileRequest.AvatarChange.UPDATE) { @@ -436,7 +436,7 @@ public class ProfileController { final ExpiringProfileKeyCredentialResponse expiringProfileKeyCredentialResponse; - if (account.getCurrentProfileVersion().map(version::equals).orElse(false)) { + if (account.getCurrentProfileVersion().map(v -> HexFormat.of().formatHex(v).equals(version)).orElse(false)) { expiringProfileKeyCredentialResponse = profilesManager.getV1(account.getUuid(), version) .map(profile -> { final ExpiringProfileKeyCredentialResponse profileKeyCredentialResponse; @@ -488,7 +488,7 @@ public class ProfileController { // Allow requests where either the version matches the latest version on Account or the latest version on Account // is empty to read the payment address. final byte[] paymentAddress = maybeProfile - .filter(p -> account.getCurrentProfileVersion().map(v -> v.equals(p.version())).orElse(true)) + .filter(p -> account.getCurrentProfileVersion().map(v -> HexFormat.of().formatHex(v).equals(p.version())).orElse(true)) .map(VersionedProfileV1::paymentAddress) .orElse(null); diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/grpc/ProfileGrpcHelper.java b/service/src/main/java/org/whispersystems/textsecuregcm/grpc/ProfileGrpcHelper.java index 76c545b17..64a7b670d 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/grpc/ProfileGrpcHelper.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/grpc/ProfileGrpcHelper.java @@ -95,7 +95,7 @@ public class ProfileGrpcHelper { // Include payment address if the version matches the latest version on Account or the latest version on Account // is empty - if (account.getCurrentProfileVersion().map(v -> v.equals(HexFormat.of().formatHex(requestVersion))).orElse(true)) { + if (account.getCurrentProfileVersion().map(v -> Arrays.equals(v, requestVersion)).orElse(true)) { final Optional paymentAddress = profile.map(VersionedProfile::paymentAddress).or(() -> v1Profile.map(VersionedProfileV1::paymentAddress)); if (paymentAddress.isPresent()) { diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/grpc/ProfileGrpcService.java b/service/src/main/java/org/whispersystems/textsecuregcm/grpc/ProfileGrpcService.java index 684909e16..6549eb236 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/grpc/ProfileGrpcService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/grpc/ProfileGrpcService.java @@ -8,6 +8,7 @@ package org.whispersystems.textsecuregcm.grpc; import com.google.protobuf.ByteString; import java.time.Clock; import java.time.ZonedDateTime; +import java.util.Arrays; import java.util.HexFormat; import java.util.List; import java.util.Map; @@ -107,11 +108,8 @@ public class ProfileGrpcService extends SimpleProfileGrpc.ProfileImplBase { validateRequest(request); - final String expectedCurrentVersionHex = HexFormat.of().formatHex(request.getExpectedCurrentVersion().toByteArray()); - - final boolean currentVersionMatchesExpected = account.getCurrentProfileVersion().isEmpty() - ? request.getExpectedCurrentVersion().isEmpty() - : account.getCurrentProfileVersion().get().equals(expectedCurrentVersionHex); + final byte[] expectedCurrentVersion = request.getExpectedCurrentVersion().toByteArray(); + final boolean currentVersionMatchesExpected = Arrays.equals(account.getCurrentProfileVersion().orElse(new byte[0]), expectedCurrentVersion); if (!currentVersionMatchesExpected) { return SetProfileResponse.newBuilder().setExpectedVersionWriteConflict(FailedPrecondition.newBuilder() @@ -177,7 +175,7 @@ public class ProfileGrpcService extends SimpleProfileGrpc.ProfileImplBase { } try { - accountsManager.updateCurrentProfileVersion(account.getIdentifier(IdentityType.ACI), version, expectedCurrentVersionHex, a -> { + accountsManager.updateCurrentProfileVersion(account.getIdentifier(IdentityType.ACI), version, expectedCurrentVersion, a -> { final List updatedBadges = Optional.of(request.getBadgeIdsList()) .map(badges -> ProfileHelper.mergeBadgeIdsWithExistingAccountBadges(clock, badgeConfigurationMap, badges, diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java index cef748dba..daca6bf58 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/Account.java @@ -8,12 +8,21 @@ package org.whispersystems.textsecuregcm.storage; import com.fasterxml.jackson.annotation.JsonFilter; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import java.io.IOException; import java.time.Clock; import java.time.Instant; import java.util.ArrayList; +import java.util.Base64; import java.util.Collections; +import java.util.HexFormat; import java.util.List; import java.util.Objects; import java.util.Optional; @@ -80,7 +89,9 @@ public class Account { private IdentityKey phoneNumberIdentityKey; @JsonProperty("cpv") - private String currentProfileVersion; + @JsonSerialize(using = ProfileKeyAdapter.Serializing.class) + @JsonDeserialize(using = ProfileKeyAdapter.Deserializing.class) + private byte[] currentProfileVersion; @JsonProperty private List badges = new ArrayList<>(); @@ -323,13 +334,13 @@ public class Account { .orElse(0L); } - public Optional getCurrentProfileVersion() { + public Optional getCurrentProfileVersion() { requireNotStale(); return Optional.ofNullable(currentProfileVersion); } - public void setCurrentProfileVersion(final String currentProfileVersion) { + public void setCurrentProfileVersion(final byte[] currentProfileVersion) { requireNotStale(); this.currentProfileVersion = currentProfileVersion; @@ -564,4 +575,26 @@ public class Account { logger.error("Accessor called on stale account", new RuntimeException()); } } + + private static class ProfileKeyAdapter { + private static class Deserializing extends JsonDeserializer { + @Override + public byte[] deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + final String val = jsonParser.getValueAsString(); + if (val.length() == 64) { + return HexFormat.of().parseHex(val); + } else { + return Base64.getDecoder().decode(val); + } + } + } + + // after deploying this once we can get rid of it in favor of the default base64 + private static class Serializing extends JsonSerializer { + @Override + public void serialize(byte[] bytes, JsonGenerator jsonGenerator, SerializerProvider serializaterProvider) throws IOException { + jsonGenerator.writeString(HexFormat.of().formatHex(bytes)); + } + } + } } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java index eae1de94c..dc6503e56 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/storage/AccountsManager.java @@ -967,7 +967,7 @@ public class AccountsManager extends RedisPubSubAdapter implemen /// /// @throws WriteConflictException if the expected current version does not match the current version public Account updateCurrentProfileVersion(final UUID accountIdentifier, final byte[] newVersion, - final String expectedCurrentVersion, final Consumer updater) throws WriteConflictException { + final byte[] expectedCurrentVersion, final Consumer updater) throws WriteConflictException { Objects.requireNonNull(expectedCurrentVersion, "expectedCurrentVersion"); @@ -975,22 +975,20 @@ public class AccountsManager extends RedisPubSubAdapter implemen final Account account = accounts.getByAccountIdentifier(accountIdentifier) .orElseThrow(() -> new IllegalArgumentException("Account not found: " + accountIdentifier)); - final String newVersionHex = HexFormat.of().formatHex(newVersion); - return accountLockManager.withLock(Set.of(account.getPhoneNumberIdentifier()), () -> { final Account maybeUpdatedAccount = update(accountIdentifier, a -> { - if (!a.getCurrentProfileVersion().orElse("").equals(expectedCurrentVersion)) { + if (!a.getCurrentProfileVersion().orElse(new byte[0]).equals(expectedCurrentVersion)) { return false; } - a.setCurrentProfileVersion(newVersionHex); + a.setCurrentProfileVersion(newVersion); updater.accept(a); return true; }); - if (!maybeUpdatedAccount.getCurrentProfileVersion().map(v -> v.equals(newVersionHex)).orElse(false)) { + if (!maybeUpdatedAccount.getCurrentProfileVersion().map(v -> v.equals(newVersion)).orElse(false)) { throw new WriteConflictException(); } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/ProfileControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/ProfileControllerTest.java index c77210f99..15e04e133 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/controllers/ProfileControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/controllers/ProfileControllerTest.java @@ -950,13 +950,14 @@ class ProfileControllerTest { final byte[] name = TestRandomUtil.nextBytes(81); final byte[] paymentAddress = TestRandomUtil.nextBytes(582); - final String version = versionHex("someversion"); + final byte[] version = version("someversion"); + final String versionHex = HexFormat.of().formatHex(version); try (final Response response = resources.getJerseyTest() .target("/v1/profile") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) .put(Entity.entity( - new CreateProfileRequest(commitment, version, name, null, null, paymentAddress, false, false, + new CreateProfileRequest(commitment, versionHex, name, null, null, paymentAddress, false, false, Optional.of(List.of()), null), MediaType.APPLICATION_JSON_TYPE))) { assertThat(response.getStatus()).isEqualTo(200); @@ -969,13 +970,14 @@ class ProfileControllerTest { @Test void testGetProfileReturnsNoPaymentAddressIfCurrentVersionMismatch() { final byte[] paymentAddress = TestRandomUtil.nextBytes(582); - final String version = versionHex("validversion"); - when(profilesManager.getV1(AuthHelper.VALID_UUID_TWO, version)).thenReturn( - Optional.of(new VersionedProfileV1(version, null, null, null, null, paymentAddress, null, null))); + final byte[] version = version("validversion"); + final String versionHex = HexFormat.of().formatHex(version); + when(profilesManager.getV1(AuthHelper.VALID_UUID_TWO, versionHex)).thenReturn( + Optional.of(new VersionedProfileV1(versionHex, null, null, null, null, paymentAddress, null, null))); { final VersionedProfileResponse profile = resources.getJerseyTest() - .target("/v1/profile/" + AuthHelper.VALID_UUID_TWO + "/" + version) + .target("/v1/profile/" + AuthHelper.VALID_UUID_TWO + "/" + versionHex) .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) .get(VersionedProfileResponse.class); @@ -987,7 +989,7 @@ class ProfileControllerTest { { final VersionedProfileResponse profile = resources.getJerseyTest() - .target("/v1/profile/" + AuthHelper.VALID_UUID_TWO + "/" + version) + .target("/v1/profile/" + AuthHelper.VALID_UUID_TWO + "/" + versionHex) .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) .get(VersionedProfileResponse.class); @@ -995,11 +997,11 @@ class ProfileControllerTest { assertThat(profile.paymentAddress()).containsExactly(paymentAddress); } - when(profileAccount.getCurrentProfileVersion()).thenReturn(Optional.of(versionHex("someotherversion"))); + when(profileAccount.getCurrentProfileVersion()).thenReturn(Optional.of(version("someotherversion"))); { final VersionedProfileResponse profile = resources.getJerseyTest() - .target("/v1/profile/" + AuthHelper.VALID_UUID_TWO + "/" + version) + .target("/v1/profile/" + AuthHelper.VALID_UUID_TWO + "/" + versionHex) .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) .get(VersionedProfileResponse.class); @@ -1013,13 +1015,13 @@ class ProfileControllerTest { final Account account = mock(Account.class); when(account.getUuid()).thenReturn(AuthHelper.VALID_UUID); when(account.getIdentifier(IdentityType.ACI)).thenReturn(AuthHelper.VALID_UUID); - when(account.getCurrentProfileVersion()).thenReturn(Optional.of(versionHex("version"))); + when(account.getCurrentProfileVersion()).thenReturn(Optional.of(version("version"))); when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(account)); when(profilesManager.getV1(any(), any())).thenReturn(Optional.empty()); final ExpiringProfileKeyCredentialProfileResponse profile = resources.getJerseyTest() - .target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, versionHex("version-that-does-not-exist"), "credential-request")) + .target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, HexFormat.of().formatHex(version("version-that-does-not-exist")), "credential-request")) .queryParam("credentialType", "expiringProfileKey") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) @@ -1043,12 +1045,13 @@ class ProfileControllerTest { final byte[] emoji = TestRandomUtil.nextBytes(60); final byte[] about = TestRandomUtil.nextBytes(156); - final String version = versionHex("anotherversion"); + final byte[] version = version("anotherversion"); + final String versionHex = HexFormat.of().formatHex(version); try (final Response response = resources.getJerseyTest() .target("/v1/profile/") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) - .put(Entity.entity(new CreateProfileRequest(commitment, version, name, emoji, about, null, false, false, + .put(Entity.entity(new CreateProfileRequest(commitment, versionHex, name, emoji, about, null, false, false, Optional.of(List.of("TEST2")), null), MediaType.APPLICATION_JSON_TYPE))) { assertThat(response.getStatus()).isEqualTo(200); @@ -1071,7 +1074,7 @@ class ProfileControllerTest { .target("/v1/profile/") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) - .put(Entity.entity(new CreateProfileRequest(commitment, version, name, emoji, about, null, false, false, + .put(Entity.entity(new CreateProfileRequest(commitment, versionHex, name, emoji, about, null, false, false, Optional.of(List.of("TEST3", "TEST2")), null), MediaType.APPLICATION_JSON_TYPE))) { assertThat(response.getStatus()).isEqualTo(200); @@ -1097,7 +1100,7 @@ class ProfileControllerTest { .target("/v1/profile/") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) - .put(Entity.entity(new CreateProfileRequest(commitment, version, name, emoji, about, null, false, false, + .put(Entity.entity(new CreateProfileRequest(commitment, versionHex, name, emoji, about, null, false, false, Optional.of(List.of("TEST2", "TEST3")), null), MediaType.APPLICATION_JSON_TYPE))) { assertThat(response.getStatus()).isEqualTo(200); @@ -1123,7 +1126,7 @@ class ProfileControllerTest { .target("/v1/profile/") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) - .put(Entity.entity(new CreateProfileRequest(commitment, version, name, emoji, about, null, false, false, + .put(Entity.entity(new CreateProfileRequest(commitment, versionHex, name, emoji, about, null, false, false, Optional.of(List.of("TEST1")), null), MediaType.APPLICATION_JSON_TYPE))) { assertThat(response.getStatus()).isEqualTo(200); @@ -1150,7 +1153,8 @@ class ProfileControllerTest { final byte[] name = TestRandomUtil.nextBytes(81); final byte[] emoji = TestRandomUtil.nextBytes(60); final byte[] about = TestRandomUtil.nextBytes(156); - final String version = versionHex("anotherversion"); + final byte[] version = version("anotherversion"); + final String versionHex = HexFormat.of().formatHex(version); clearInvocations(AuthHelper.VALID_ACCOUNT_TWO); reset(accountsManager); @@ -1173,7 +1177,7 @@ class ProfileControllerTest { .target("/v1/profile/") .request() .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) - .put(Entity.entity(new CreateProfileRequest(commitment, version, name, emoji, about, null, false, false, + .put(Entity.entity(new CreateProfileRequest(commitment, versionHex, name, emoji, about, null, false, false, Optional.of(List.of("TEST1")), null), MediaType.APPLICATION_JSON_TYPE))) { assertThat(response.getStatus()).isEqualTo(200); @@ -1198,7 +1202,8 @@ class ProfileControllerTest { @MethodSource void testGetProfileWithExpiringProfileKeyCredential(final MultivaluedMap authHeaders) throws VerificationFailedException, InvalidInputException { - final String version = versionHex("version"); + final byte[] version = version("version"); + final String versionHex = HexFormat.of().formatHex(version); final ServerSecretParams serverSecretParams = ServerSecretParams.generate(); final ServerPublicParams serverPublicParams = serverSecretParams.getPublicParams(); @@ -1233,12 +1238,12 @@ class ProfileControllerTest { serverZkProfile.issueExpiringProfileKeyCredential(credentialRequest, new ServiceId.Aci(AuthHelper.VALID_UUID), profileKeyCommitment, expiration); when(accountsManager.getByServiceIdentifier(new AciServiceIdentifier(AuthHelper.VALID_UUID))).thenReturn(Optional.of(account)); - when(profilesManager.getV1(AuthHelper.VALID_UUID, version)).thenReturn(Optional.of(versionedProfile)); + when(profilesManager.getV1(AuthHelper.VALID_UUID, versionHex)).thenReturn(Optional.of(versionedProfile)); when(zkProfileOperations.issueExpiringProfileKeyCredential(eq(credentialRequest), eq(new ServiceId.Aci(AuthHelper.VALID_UUID)), eq(profileKeyCommitment), any())) .thenReturn(credentialResponse); final ExpiringProfileKeyCredentialProfileResponse profile = resources.getJerseyTest() - .target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, version, + .target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, versionHex, HexFormat.of().formatHex(credentialRequest.serialize()))) .queryParam("credentialType", "expiringProfileKey") .request() @@ -1267,7 +1272,8 @@ class ProfileControllerTest { @Test void testGetProfileWithExpiringProfileKeyCredentialBadRequest() throws VerificationFailedException, InvalidInputException { - final String version = versionHex("version"); + final byte[] version = version("version"); + final String versionHex = HexFormat.of().formatHex(version); final ServerSecretParams serverSecretParams = ServerSecretParams.generate(); final ServerPublicParams serverPublicParams = serverSecretParams.getPublicParams(); @@ -1294,12 +1300,12 @@ class ProfileControllerTest { when(account.getCurrentProfileVersion()).thenReturn(Optional.of(version)); when(accountsManager.getByServiceIdentifier(new AciServiceIdentifier(AuthHelper.VALID_UUID))).thenReturn(Optional.of(account)); - when(profilesManager.getV1(AuthHelper.VALID_UUID, version)).thenReturn(Optional.of(versionedProfile)); + when(profilesManager.getV1(AuthHelper.VALID_UUID, versionHex)).thenReturn(Optional.of(versionedProfile)); when(zkProfileOperations.issueExpiringProfileKeyCredential(any(), any(), any(), any())) .thenThrow(new VerificationFailedException()); final Response response = resources.getJerseyTest() - .target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, version, + .target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, versionHex, HexFormat.of().formatHex(credentialRequest.serialize()))) .queryParam("credentialType", "expiringProfileKey") .request() @@ -1312,7 +1318,8 @@ class ProfileControllerTest { @Test void testGetProfileWithExpiringProfileKeyCredentialNonCurrentVersion() throws VerificationFailedException, InvalidInputException { - final String version = versionHex("version"); + final byte[] version = version("version"); + final String versionHex = HexFormat.of().formatHex(version); final ServerSecretParams serverSecretParams = ServerSecretParams.generate(); final ServerPublicParams serverPublicParams = serverSecretParams.getPublicParams(); @@ -1338,13 +1345,13 @@ class ProfileControllerTest { when(account.getUuid()).thenReturn(AuthHelper.VALID_UUID); when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of(UNIDENTIFIED_ACCESS_KEY)); when(account.isIdentifiedBy(new AciServiceIdentifier(AuthHelper.VALID_UUID))).thenReturn(true); - when(account.getCurrentProfileVersion()).thenReturn(Optional.of(versionHex("the-current-version"))); + when(account.getCurrentProfileVersion()).thenReturn(Optional.of(version("the-current-version"))); when(accountsManager.getByServiceIdentifier(new AciServiceIdentifier(AuthHelper.VALID_UUID))).thenReturn(Optional.of(account)); - when(profilesManager.getV1(AuthHelper.VALID_UUID, version)).thenReturn(Optional.of(versionedProfile)); + when(profilesManager.getV1(AuthHelper.VALID_UUID, versionHex)).thenReturn(Optional.of(versionedProfile)); final Response response = resources.getJerseyTest() - .target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, version, + .target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, versionHex, HexFormat.of().formatHex(credentialRequest.serialize()))) .queryParam("credentialType", "expiringProfileKey") .request() @@ -1580,11 +1587,15 @@ class ProfileControllerTest { } } - private static String versionHex(final String versionString) { + private static byte[] version(final String versionString) { try { - return HexFormat.of().formatHex(MessageDigest.getInstance("SHA-256").digest(versionString.getBytes(StandardCharsets.UTF_8))); + return MessageDigest.getInstance("SHA-256").digest(versionString.getBytes(StandardCharsets.UTF_8)); } catch (NoSuchAlgorithmException e) { throw new AssertionError(e); } } + + private static String versionHex(final String versionString) { + return HexFormat.of().formatHex(version(versionString)); + } } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/grpc/ProfileAnonymousGrpcServiceTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/grpc/ProfileAnonymousGrpcServiceTest.java index 612765c2e..be0807665 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/grpc/ProfileAnonymousGrpcServiceTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/grpc/ProfileAnonymousGrpcServiceTest.java @@ -372,7 +372,7 @@ public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest profile = Optional.of(new VersionedProfileV1(HexFormat.of().formatHex(requestVersion), name, avatar, emoji, about, paymentAddress, phoneNumberSharing, new byte[0])); - when(account.getCurrentProfileVersion()).thenReturn(Optional.ofNullable(accountVersion).map(v -> HexFormat.of().formatHex(v))); + when(account.getCurrentProfileVersion()).thenReturn(Optional.ofNullable(accountVersion)); when(account.isUnrestrictedUnidentifiedAccess()).thenReturn(false); when(account.getUnidentifiedAccessKey()).thenReturn(Optional.of(unidentifiedAccessKey)); @@ -428,10 +428,9 @@ public class ProfileAnonymousGrpcServiceTest extends SimpleBaseGrpcTest HexFormat.of().formatHex(v))); + when(account.getCurrentProfileVersion()).thenReturn(Optional.ofNullable(accountVersion)); when(accountsManager.getByServiceIdentifier(any())).thenReturn(Optional.of(account)); when(profilesManager.getV1(any(), any())).thenReturn(profile); @@ -757,7 +757,6 @@ public class ProfileGrpcServiceTest extends SimpleBaseGrpcTest assertEquals(discoverableByPhoneNumber, account.isDiscoverableByPhoneNumber()), - () -> assertEquals(currentProfileVersion, account.getCurrentProfileVersion().orElseThrow()), + () -> assertArrayEquals(currentProfileVersion, account.getCurrentProfileVersion().orElseThrow()), () -> assertEquals(identityKey, account.getIdentityKey(IdentityType.ACI)), () -> assertArrayEquals(unidentifiedAccessKey, account.getUnidentifiedAccessKey().orElseThrow()), () -> assertTrue(account.getRegistrationLock().verify(clientRegistrationLock)), diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java index be66e52ac..ae2aab0f6 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsManagerTest.java @@ -1460,22 +1460,22 @@ class AccountsManagerTest { final UUID accountIdentifier = account.getIdentifier(IdentityType.ACI); addRetrievableAccount(account); - account.setCurrentProfileVersion(HexFormat.of().formatHex(currentVersion)); + account.setCurrentProfileVersion(currentVersion); final AccountBadge badge = new AccountBadge("test", CLOCK.instant().plusSeconds(60), true); assertTrue(account.getBadges().isEmpty()); if (expectException) { - assertThrows(WriteConflictException.class, () -> accountsManager.updateCurrentProfileVersion(accountIdentifier, newVersion, HexFormat.of().formatHex(expectedVersion), _ -> {})); + assertThrows(WriteConflictException.class, () -> accountsManager.updateCurrentProfileVersion(accountIdentifier, newVersion, expectedVersion, _ -> {})); } else { final Account updatedAccount = accountsManager.updateCurrentProfileVersion(accountIdentifier, newVersion, - HexFormat.of().formatHex(expectedVersion), a -> { + expectedVersion, a -> { a.setBadges(CLOCK, new ArrayList<>(List.of(badge))); }); - assertArrayEquals(newVersion, HexFormat.of().parseHex(updatedAccount.getCurrentProfileVersion().orElseThrow())); + assertArrayEquals(newVersion, updatedAccount.getCurrentProfileVersion().orElseThrow()); assertEquals(List.of(badge), updatedAccount.getBadges()); } } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsTest.java index 6e19c2966..e3985c482 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/storage/AccountsTest.java @@ -1146,7 +1146,7 @@ class AccountsTest { final Account secondAccountInstance = accounts.getByAccountIdentifier(firstAccountInstance.getUuid()).orElseThrow(); // update via the first instance, which will update the version - firstAccountInstance.setCurrentProfileVersion("1"); + firstAccountInstance.setCurrentProfileVersion(new byte[32]); accounts.update(firstAccountInstance); assertThrows(ContestedOptimisticLockException.class,