Add phone-number-sharing field to versioned profile
Co-authored-by: Katherine <katherine@signal.org>
This commit is contained in:
		
							parent
							
								
									3b509bf820
								
							
						
					
					
						commit
						9d3d4a3698
					
				|  | @ -163,9 +163,9 @@ public class ProfileController { | ||||||
|   public Response setProfile(@Auth AuthenticatedAccount auth, @NotNull @Valid CreateProfileRequest request) { |   public Response setProfile(@Auth AuthenticatedAccount auth, @NotNull @Valid CreateProfileRequest request) { | ||||||
| 
 | 
 | ||||||
|     final Optional<VersionedProfile> currentProfile = profilesManager.get(auth.getAccount().getUuid(), |     final Optional<VersionedProfile> currentProfile = profilesManager.get(auth.getAccount().getUuid(), | ||||||
|         request.getVersion()); |         request.version()); | ||||||
| 
 | 
 | ||||||
|     if (request.getPaymentAddress() != null && request.getPaymentAddress().length != 0) { |     if (request.paymentAddress() != null && request.paymentAddress().length != 0) { | ||||||
|       final boolean hasDisallowedPrefix = |       final boolean hasDisallowedPrefix = | ||||||
|           dynamicConfigurationManager.getConfiguration().getPaymentsConfiguration().getDisallowedPrefixes().stream() |           dynamicConfigurationManager.getConfiguration().getPaymentsConfiguration().getDisallowedPrefixes().stream() | ||||||
|               .anyMatch(prefix -> auth.getAccount().getNumber().startsWith(prefix)); |               .anyMatch(prefix -> auth.getAccount().getNumber().startsWith(prefix)); | ||||||
|  | @ -189,13 +189,14 @@ public class ProfileController { | ||||||
| 
 | 
 | ||||||
|     profilesManager.set(auth.getAccount().getUuid(), |     profilesManager.set(auth.getAccount().getUuid(), | ||||||
|         new VersionedProfile( |         new VersionedProfile( | ||||||
|             request.getVersion(), |             request.version(), | ||||||
|             request.getName(), |             request.name(), | ||||||
|             avatar, |             avatar, | ||||||
|             request.getAboutEmoji(), |             request.aboutEmoji(), | ||||||
|             request.getAbout(), |             request.about(), | ||||||
|             request.getPaymentAddress(), |             request.paymentAddress(), | ||||||
|             request.getCommitment().serialize())); |             request.phoneNumberSharing(), | ||||||
|  |             request.commitment().serialize())); | ||||||
| 
 | 
 | ||||||
|     if (request.getAvatarChange() != CreateProfileRequest.AvatarChange.UNCHANGED) { |     if (request.getAvatarChange() != CreateProfileRequest.AvatarChange.UNCHANGED) { | ||||||
|       currentAvatar.ifPresent(s -> s3client.deleteObject(DeleteObjectRequest.builder() |       currentAvatar.ifPresent(s -> s3client.deleteObject(DeleteObjectRequest.builder() | ||||||
|  | @ -204,13 +205,13 @@ public class ProfileController { | ||||||
|           .build())); |           .build())); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     final List<AccountBadge> updatedBadges = request.getBadges() |     final List<AccountBadge> updatedBadges = request.badges() | ||||||
|         .map(badges -> ProfileHelper.mergeBadgeIdsWithExistingAccountBadges(clock, badgeConfigurationMap, badges, auth.getAccount().getBadges())) |         .map(badges -> ProfileHelper.mergeBadgeIdsWithExistingAccountBadges(clock, badgeConfigurationMap, badges, auth.getAccount().getBadges())) | ||||||
|         .orElseGet(() -> auth.getAccount().getBadges()); |         .orElseGet(() -> auth.getAccount().getBadges()); | ||||||
| 
 | 
 | ||||||
|     accountsManager.update(auth.getAccount(), a -> { |     accountsManager.update(auth.getAccount(), a -> { | ||||||
|       a.setBadges(clock, updatedBadges); |       a.setBadges(clock, updatedBadges); | ||||||
|       a.setCurrentProfileVersion(request.getVersion()); |       a.setCurrentProfileVersion(request.version()); | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     if (request.getAvatarChange() == CreateProfileRequest.AvatarChange.UPDATE) { |     if (request.getAvatarChange() == CreateProfileRequest.AvatarChange.UPDATE) { | ||||||
|  | @ -411,6 +412,7 @@ public class ProfileController { | ||||||
|     final byte[] about = maybeProfile.map(VersionedProfile::about).orElse(null); |     final byte[] about = maybeProfile.map(VersionedProfile::about).orElse(null); | ||||||
|     final byte[] aboutEmoji = maybeProfile.map(VersionedProfile::aboutEmoji).orElse(null); |     final byte[] aboutEmoji = maybeProfile.map(VersionedProfile::aboutEmoji).orElse(null); | ||||||
|     final String avatar = maybeProfile.map(VersionedProfile::avatar).orElse(null); |     final String avatar = maybeProfile.map(VersionedProfile::avatar).orElse(null); | ||||||
|  |     final byte[] phoneNumberSharing = maybeProfile.map(VersionedProfile::phoneNumberSharing).orElse(null); | ||||||
| 
 | 
 | ||||||
|     // Allow requests where either the version matches the latest version on Account or the latest version on Account |     // 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. |     // is empty to read the payment address. | ||||||
|  | @ -421,7 +423,7 @@ public class ProfileController { | ||||||
| 
 | 
 | ||||||
|     return new VersionedProfileResponse( |     return new VersionedProfileResponse( | ||||||
|         buildBaseProfileResponseForAccountIdentity(account, isSelf, containerRequestContext), |         buildBaseProfileResponseForAccountIdentity(account, isSelf, containerRequestContext), | ||||||
|         name, about, aboutEmoji, avatar, paymentAddress); |         name, about, aboutEmoji, avatar, paymentAddress, phoneNumberSharing); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private BaseProfileResponse buildBaseProfileResponseForAccountIdentity(final Account account, |   private BaseProfileResponse buildBaseProfileResponseForAccountIdentity(final Account account, | ||||||
|  |  | ||||||
|  | @ -10,91 +10,62 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; | ||||||
| import com.fasterxml.jackson.databind.annotation.JsonSerialize; | import com.fasterxml.jackson.databind.annotation.JsonSerialize; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Optional; | import java.util.Optional; | ||||||
| import javax.annotation.Nullable; |  | ||||||
| import javax.validation.constraints.NotEmpty; | import javax.validation.constraints.NotEmpty; | ||||||
| import javax.validation.constraints.NotNull; | import javax.validation.constraints.NotNull; | ||||||
| import org.signal.libsignal.zkgroup.profiles.ProfileKeyCommitment; | import org.signal.libsignal.zkgroup.profiles.ProfileKeyCommitment; | ||||||
| import org.whispersystems.textsecuregcm.util.ByteArrayBase64WithPaddingAdapter; | import org.whispersystems.textsecuregcm.util.ByteArrayBase64WithPaddingAdapter; | ||||||
| import org.whispersystems.textsecuregcm.util.ExactlySize; | import org.whispersystems.textsecuregcm.util.ExactlySize; | ||||||
| 
 | 
 | ||||||
| public class CreateProfileRequest { | public record CreateProfileRequest( | ||||||
|  |   @JsonProperty | ||||||
|  |   @NotNull | ||||||
|  |   @JsonDeserialize(using = ProfileKeyCommitmentAdapter.Deserializing.class) | ||||||
|  |   @JsonSerialize(using = ProfileKeyCommitmentAdapter.Serializing.class) | ||||||
|  |   ProfileKeyCommitment commitment, | ||||||
| 
 | 
 | ||||||
|   @JsonProperty |   @JsonProperty | ||||||
|   @NotEmpty |   @NotEmpty | ||||||
|   private String version; |   String version, | ||||||
| 
 | 
 | ||||||
|   @JsonProperty |   @JsonProperty | ||||||
|   @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) |   @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) | ||||||
|   @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) |   @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) | ||||||
|   @ExactlySize({81, 285}) |   @ExactlySize({81, 285}) | ||||||
|   private byte[] name; |   byte[] name, | ||||||
| 
 |  | ||||||
|   @JsonProperty |  | ||||||
|   private boolean avatar; |  | ||||||
| 
 |  | ||||||
|   @JsonProperty |  | ||||||
|   private boolean sameAvatar; |  | ||||||
| 
 | 
 | ||||||
|   @JsonProperty |   @JsonProperty | ||||||
|   @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) |   @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) | ||||||
|   @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) |   @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) | ||||||
|   @ExactlySize({0, 60}) |   @ExactlySize({0, 60}) | ||||||
|   private byte[] aboutEmoji; |   byte[] aboutEmoji, | ||||||
| 
 | 
 | ||||||
|   @JsonProperty |   @JsonProperty | ||||||
|   @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) |   @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) | ||||||
|   @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) |   @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) | ||||||
|   @ExactlySize({0, 156, 282, 540}) |   @ExactlySize({0, 156, 282, 540}) | ||||||
|   private byte[] about; |   byte[] about, | ||||||
| 
 | 
 | ||||||
|   @JsonProperty |   @JsonProperty | ||||||
|   @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) |   @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) | ||||||
|   @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) |   @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) | ||||||
|   @ExactlySize({0, 582}) |   @ExactlySize({0, 582}) | ||||||
|   private byte[] paymentAddress; |   byte[] paymentAddress, | ||||||
|  | 
 | ||||||
|  |   @JsonProperty("avatar") | ||||||
|  |   boolean hasAvatar, | ||||||
| 
 | 
 | ||||||
|   @JsonProperty |   @JsonProperty | ||||||
|   @Nullable |   boolean sameAvatar, | ||||||
|   private List<String> badgeIds; | 
 | ||||||
|  |   @JsonProperty("badgeIds") | ||||||
|  |   Optional<List<String>> badges, | ||||||
| 
 | 
 | ||||||
|   @JsonProperty |   @JsonProperty | ||||||
|   @NotNull |   @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) | ||||||
|   @JsonDeserialize(using = ProfileKeyCommitmentAdapter.Deserializing.class) |   @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) | ||||||
|   @JsonSerialize(using = ProfileKeyCommitmentAdapter.Serializing.class) |   @ExactlySize({0, 29}) | ||||||
|   private ProfileKeyCommitment commitment; |   byte[] phoneNumberSharing | ||||||
| 
 | ) { | ||||||
|   public CreateProfileRequest() { |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public CreateProfileRequest( |  | ||||||
|       final ProfileKeyCommitment commitment, final String version, final byte[] name, final byte[] aboutEmoji, final byte[] about, |  | ||||||
|       final byte[] paymentAddress, final boolean wantsAvatar, final boolean sameAvatar, final List<String> badgeIds) { |  | ||||||
|     this.commitment = commitment; |  | ||||||
|     this.version = version; |  | ||||||
|     this.name = name; |  | ||||||
|     this.aboutEmoji = aboutEmoji; |  | ||||||
|     this.about = about; |  | ||||||
|     this.paymentAddress = paymentAddress; |  | ||||||
|     this.avatar = wantsAvatar; |  | ||||||
|     this.sameAvatar = sameAvatar; |  | ||||||
|     this.badgeIds = badgeIds; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public ProfileKeyCommitment getCommitment() { |  | ||||||
|     return commitment; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public String getVersion() { |  | ||||||
|     return version; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public byte[] getName() { |  | ||||||
|     return name; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public boolean hasAvatar() { |  | ||||||
|     return avatar; |  | ||||||
|   } |  | ||||||
| 
 | 
 | ||||||
|   public enum AvatarChange { |   public enum AvatarChange { | ||||||
|     UNCHANGED, |     UNCHANGED, | ||||||
|  | @ -112,19 +83,4 @@ public class CreateProfileRequest { | ||||||
|     return AvatarChange.UNCHANGED; |     return AvatarChange.UNCHANGED; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public byte[] getAboutEmoji() { |  | ||||||
|     return aboutEmoji; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public byte[] getAbout() { |  | ||||||
|     return about; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public byte[] getPaymentAddress() { |  | ||||||
|     return paymentAddress; |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
|   public Optional<List<String>> getBadges() { |  | ||||||
|     return Optional.ofNullable(badgeIds); |  | ||||||
|   } |  | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -11,6 +11,8 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize; | ||||||
| import com.fasterxml.jackson.databind.annotation.JsonSerialize; | import com.fasterxml.jackson.databind.annotation.JsonSerialize; | ||||||
| import org.whispersystems.textsecuregcm.util.ByteArrayBase64WithPaddingAdapter; | import org.whispersystems.textsecuregcm.util.ByteArrayBase64WithPaddingAdapter; | ||||||
| 
 | 
 | ||||||
|  | // Note, this class cannot be converted into a record because @JsonUnwrapped does not work with records. | ||||||
|  | // https://github.com/FasterXML/jackson-databind/issues/1467 | ||||||
| public class VersionedProfileResponse { | public class VersionedProfileResponse { | ||||||
| 
 | 
 | ||||||
|   @JsonUnwrapped |   @JsonUnwrapped | ||||||
|  | @ -39,6 +41,11 @@ public class VersionedProfileResponse { | ||||||
|   @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) |   @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) | ||||||
|   private byte[] paymentAddress; |   private byte[] paymentAddress; | ||||||
| 
 | 
 | ||||||
|  |   @JsonProperty | ||||||
|  |   @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) | ||||||
|  |   @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) | ||||||
|  |   private byte[] phoneNumberSharing; | ||||||
|  | 
 | ||||||
|   public VersionedProfileResponse() { |   public VersionedProfileResponse() { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -47,7 +54,8 @@ public class VersionedProfileResponse { | ||||||
|       final byte[] about, |       final byte[] about, | ||||||
|       final byte[] aboutEmoji, |       final byte[] aboutEmoji, | ||||||
|       final String avatar, |       final String avatar, | ||||||
|       final byte[] paymentAddress) { |       final byte[] paymentAddress, | ||||||
|  |       final byte[] phoneNumberSharing) { | ||||||
| 
 | 
 | ||||||
|     this.baseProfileResponse = baseProfileResponse; |     this.baseProfileResponse = baseProfileResponse; | ||||||
|     this.name = name; |     this.name = name; | ||||||
|  | @ -55,6 +63,7 @@ public class VersionedProfileResponse { | ||||||
|     this.aboutEmoji = aboutEmoji; |     this.aboutEmoji = aboutEmoji; | ||||||
|     this.avatar = avatar; |     this.avatar = avatar; | ||||||
|     this.paymentAddress = paymentAddress; |     this.paymentAddress = paymentAddress; | ||||||
|  |     this.phoneNumberSharing = phoneNumberSharing; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   public BaseProfileResponse getBaseProfileResponse() { |   public BaseProfileResponse getBaseProfileResponse() { | ||||||
|  | @ -80,4 +89,8 @@ public class VersionedProfileResponse { | ||||||
|   public byte[] getPaymentAddress() { |   public byte[] getPaymentAddress() { | ||||||
|     return paymentAddress; |     return paymentAddress; | ||||||
|   } |   } | ||||||
|  | 
 | ||||||
|  |   public byte[] getPhoneNumberSharing() { | ||||||
|  |     return phoneNumberSharing; | ||||||
|  |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -48,6 +48,7 @@ public class ProfileGrpcHelper { | ||||||
|           maybeProfile.map(VersionedProfile::about).map(ByteString::copyFrom).ifPresent(responseBuilder::setAbout); |           maybeProfile.map(VersionedProfile::about).map(ByteString::copyFrom).ifPresent(responseBuilder::setAbout); | ||||||
|           maybeProfile.map(VersionedProfile::aboutEmoji).map(ByteString::copyFrom).ifPresent(responseBuilder::setAboutEmoji); |           maybeProfile.map(VersionedProfile::aboutEmoji).map(ByteString::copyFrom).ifPresent(responseBuilder::setAboutEmoji); | ||||||
|           maybeProfile.map(VersionedProfile::avatar).ifPresent(responseBuilder::setAvatar); |           maybeProfile.map(VersionedProfile::avatar).ifPresent(responseBuilder::setAvatar); | ||||||
|  |           maybeProfile.map(VersionedProfile::phoneNumberSharing).map(ByteString::copyFrom).ifPresent(responseBuilder::setPhoneNumberSharing); | ||||||
| 
 | 
 | ||||||
|           // Allow requests where either the version matches the latest version on Account or the latest version on Account |           // 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. |           // is empty to read the payment address. | ||||||
|  |  | ||||||
|  | @ -141,6 +141,7 @@ public class ProfileGrpcService extends ReactorProfileGrpc.ProfileImplBase { | ||||||
|                   request.getAboutEmoji().toByteArray(), |                   request.getAboutEmoji().toByteArray(), | ||||||
|                   request.getAbout().toByteArray(), |                   request.getAbout().toByteArray(), | ||||||
|                   request.getPaymentAddress().toByteArray(), |                   request.getPaymentAddress().toByteArray(), | ||||||
|  |                   request.getPhoneNumberSharing().toByteArray(), | ||||||
|                   request.getCommitment().toByteArray()))); |                   request.getCommitment().toByteArray()))); | ||||||
| 
 | 
 | ||||||
|           final List<Mono<?>> updates = new ArrayList<>(2); |           final List<Mono<?>> updates = new ArrayList<>(2); | ||||||
|  |  | ||||||
|  | @ -62,6 +62,9 @@ public class Profiles { | ||||||
|   // Payment address; byte array |   // Payment address; byte array | ||||||
|   private static final String ATTR_PAYMENT_ADDRESS = "P"; |   private static final String ATTR_PAYMENT_ADDRESS = "P"; | ||||||
| 
 | 
 | ||||||
|  |   // Phone number sharing setting; byte array | ||||||
|  |   private static final String ATTR_PHONE_NUMBER_SHARING = "S"; | ||||||
|  | 
 | ||||||
|   // Commitment; byte array |   // Commitment; byte array | ||||||
|   private static final String ATTR_COMMITMENT = "C"; |   private static final String ATTR_COMMITMENT = "C"; | ||||||
| 
 | 
 | ||||||
|  | @ -71,7 +74,8 @@ public class Profiles { | ||||||
|       "#avatar", ATTR_AVATAR, |       "#avatar", ATTR_AVATAR, | ||||||
|       "#about", ATTR_ABOUT, |       "#about", ATTR_ABOUT, | ||||||
|       "#aboutEmoji", ATTR_EMOJI, |       "#aboutEmoji", ATTR_EMOJI, | ||||||
|       "#paymentAddress", ATTR_PAYMENT_ADDRESS); |       "#paymentAddress", ATTR_PAYMENT_ADDRESS, | ||||||
|  |       "#phoneNumberSharing", ATTR_PHONE_NUMBER_SHARING); | ||||||
| 
 | 
 | ||||||
|   private static final Timer SET_PROFILES_TIMER = Metrics.timer(name(Profiles.class, "set")); |   private static final Timer SET_PROFILES_TIMER = Metrics.timer(name(Profiles.class, "set")); | ||||||
|   private static final Timer GET_PROFILE_TIMER = Metrics.timer(name(Profiles.class, "get")); |   private static final Timer GET_PROFILE_TIMER = Metrics.timer(name(Profiles.class, "get")); | ||||||
|  | @ -154,6 +158,12 @@ public class Profiles { | ||||||
|       deletedAttributes.add("paymentAddress"); |       deletedAttributes.add("paymentAddress"); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     if (profile.phoneNumberSharing() != null) { | ||||||
|  |       updatedAttributes.add("phoneNumberSharing"); | ||||||
|  |     } else { | ||||||
|  |       deletedAttributes.add("phoneNumberSharing"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     final StringBuilder updateExpressionBuilder = new StringBuilder( |     final StringBuilder updateExpressionBuilder = new StringBuilder( | ||||||
|         "SET #commitment = if_not_exists(#commitment, :commitment)"); |         "SET #commitment = if_not_exists(#commitment, :commitment)"); | ||||||
| 
 | 
 | ||||||
|  | @ -201,6 +211,9 @@ public class Profiles { | ||||||
|       expressionValues.put(":paymentAddress", AttributeValues.fromByteArray(profile.paymentAddress())); |       expressionValues.put(":paymentAddress", AttributeValues.fromByteArray(profile.paymentAddress())); | ||||||
|     } |     } | ||||||
|      |      | ||||||
|  |     if (profile.phoneNumberSharing() != null) { | ||||||
|  |       expressionValues.put(":phoneNumberSharing", AttributeValues.fromByteArray(profile.phoneNumberSharing())); | ||||||
|  |     } | ||||||
|     return expressionValues; |     return expressionValues; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -235,6 +248,7 @@ public class Profiles { | ||||||
|         getBytes(item, ATTR_EMOJI), |         getBytes(item, ATTR_EMOJI), | ||||||
|         getBytes(item, ATTR_ABOUT), |         getBytes(item, ATTR_ABOUT), | ||||||
|         getBytes(item, ATTR_PAYMENT_ADDRESS), |         getBytes(item, ATTR_PAYMENT_ADDRESS), | ||||||
|  |         getBytes(item, ATTR_PHONE_NUMBER_SHARING), | ||||||
|         AttributeValues.getByteArray(item, ATTR_COMMITMENT, null)); |         AttributeValues.getByteArray(item, ATTR_COMMITMENT, null)); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5,14 +5,10 @@ | ||||||
| 
 | 
 | ||||||
| package org.whispersystems.textsecuregcm.storage; | package org.whispersystems.textsecuregcm.storage; | ||||||
| 
 | 
 | ||||||
| import com.fasterxml.jackson.annotation.JsonCreator; |  | ||||||
| import com.fasterxml.jackson.annotation.JsonProperty; |  | ||||||
| import com.fasterxml.jackson.databind.annotation.JsonDeserialize; | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; | ||||||
| import com.fasterxml.jackson.databind.annotation.JsonSerialize; | import com.fasterxml.jackson.databind.annotation.JsonSerialize; | ||||||
| import org.whispersystems.textsecuregcm.util.ByteArrayAdapter; | import org.whispersystems.textsecuregcm.util.ByteArrayAdapter; | ||||||
| import org.whispersystems.textsecuregcm.util.ByteArrayBase64WithPaddingAdapter; | import org.whispersystems.textsecuregcm.util.ByteArrayBase64WithPaddingAdapter; | ||||||
| import java.util.Arrays; |  | ||||||
| import java.util.Objects; |  | ||||||
| 
 | 
 | ||||||
| public record VersionedProfile (String version, | public record VersionedProfile (String version, | ||||||
|                                 @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) |                                 @JsonSerialize(using = ByteArrayBase64WithPaddingAdapter.Serializing.class) | ||||||
|  | @ -33,6 +29,10 @@ public record VersionedProfile (String version, | ||||||
|                                 @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) |                                 @JsonDeserialize(using = ByteArrayBase64WithPaddingAdapter.Deserializing.class) | ||||||
|                                 byte[] paymentAddress, |                                 byte[] paymentAddress, | ||||||
| 
 | 
 | ||||||
|  |                                 @JsonSerialize(using = ByteArrayAdapter.Serializing.class) | ||||||
|  |                                 @JsonDeserialize(using = ByteArrayAdapter.Deserializing.class) | ||||||
|  |                                 byte[] phoneNumberSharing, | ||||||
|  | 
 | ||||||
|                                 @JsonSerialize(using = ByteArrayAdapter.Serializing.class) |                                 @JsonSerialize(using = ByteArrayAdapter.Serializing.class) | ||||||
|                                 @JsonDeserialize(using = ByteArrayAdapter.Deserializing.class) |                                 @JsonDeserialize(using = ByteArrayAdapter.Deserializing.class) | ||||||
|                                 byte[] commitment) {} |                                 byte[] commitment) {} | ||||||
|  |  | ||||||
|  | @ -132,6 +132,10 @@ message SetProfileRequest { | ||||||
|    * A list of badge IDs associated with the profile. |    * A list of badge IDs associated with the profile. | ||||||
|    */ |    */ | ||||||
|   repeated string badge_ids = 7; |   repeated string badge_ids = 7; | ||||||
|  |   /** | ||||||
|  |    * The ciphertext of the phone-number sharing setting on the profile. 29-byte encrypted boolean. | ||||||
|  |    */ | ||||||
|  |   bytes phone_number_sharing = 8; | ||||||
|   /** |   /** | ||||||
|    * The profile key commitment. Used to issue a profile key credential response. |    * The profile key commitment. Used to issue a profile key credential response. | ||||||
|    * Must be set on the request. |    * Must be set on the request. | ||||||
|  | @ -189,6 +193,10 @@ message GetVersionedProfileResponse { | ||||||
|    * The ciphertext of the MobileCoin wallet ID on the profile. |    * The ciphertext of the MobileCoin wallet ID on the profile. | ||||||
|    */ |    */ | ||||||
|   bytes payment_address = 5; |   bytes payment_address = 5; | ||||||
|  |   /** | ||||||
|  |    * The ciphertext of the phone-number sharing setting on the profile. | ||||||
|  |    */ | ||||||
|  |   bytes phone_number_sharing = 6; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| message GetUnversionedProfileRequest { | message GetUnversionedProfileRequest { | ||||||
|  |  | ||||||
|  | @ -227,10 +227,11 @@ class ProfileControllerTest { | ||||||
|     final byte[] name = TestRandomUtil.nextBytes(81); |     final byte[] name = TestRandomUtil.nextBytes(81); | ||||||
|     final byte[] emoji = TestRandomUtil.nextBytes(60); |     final byte[] emoji = TestRandomUtil.nextBytes(60); | ||||||
|     final byte[] about = TestRandomUtil.nextBytes(156); |     final byte[] about = TestRandomUtil.nextBytes(156); | ||||||
|  |     final byte[] phoneNumberSharing = TestRandomUtil.nextBytes(29); | ||||||
| 
 | 
 | ||||||
|     when(profilesManager.get(eq(AuthHelper.VALID_UUID), eq("someversion"))).thenReturn(Optional.empty()); |     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( |     when(profilesManager.get(eq(AuthHelper.VALID_UUID_TWO), eq("validversion"))).thenReturn(Optional.of(new VersionedProfile( | ||||||
|         "validversion", name, "profiles/validavatar", emoji, about, null, "validcommitmnet".getBytes()))); |         "validversion", name, "profiles/validavatar", emoji, about, null, phoneNumberSharing, "validcommitment".getBytes()))); | ||||||
| 
 | 
 | ||||||
|     clearInvocations(rateLimiter); |     clearInvocations(rateLimiter); | ||||||
|     clearInvocations(accountsManager); |     clearInvocations(accountsManager); | ||||||
|  | @ -419,7 +420,7 @@ class ProfileControllerTest { | ||||||
|         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) |         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) | ||||||
|         .put(Entity.entity(new CreateProfileRequest(commitment, "someversion", |         .put(Entity.entity(new CreateProfileRequest(commitment, "someversion", | ||||||
|             name, null, null, |             name, null, null, | ||||||
|             null, true, false, List.of()), MediaType.APPLICATION_JSON_TYPE), ProfileAvatarUploadAttributes.class); |             null, true, false, Optional.of(List.of()), null), MediaType.APPLICATION_JSON_TYPE), ProfileAvatarUploadAttributes.class); | ||||||
| 
 | 
 | ||||||
|     final ArgumentCaptor<VersionedProfile> profileArgumentCaptor = ArgumentCaptor.forClass(VersionedProfile.class); |     final ArgumentCaptor<VersionedProfile> profileArgumentCaptor = ArgumentCaptor.forClass(VersionedProfile.class); | ||||||
| 
 | 
 | ||||||
|  | @ -446,7 +447,7 @@ class ProfileControllerTest { | ||||||
|         .request() |         .request() | ||||||
|         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) |         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) | ||||||
|         .put(Entity.entity(new CreateProfileRequest(commitment, "someversion", name, |         .put(Entity.entity(new CreateProfileRequest(commitment, "someversion", name, | ||||||
|             null, null, null, true, false, List.of()), MediaType.APPLICATION_JSON_TYPE))) { |             null, null, null, true, false, Optional.of(List.of()), null), MediaType.APPLICATION_JSON_TYPE))) { | ||||||
| 
 | 
 | ||||||
|       assertThat(response.getStatus()).isEqualTo(422); |       assertThat(response.getStatus()).isEqualTo(422); | ||||||
|     } |     } | ||||||
|  | @ -456,6 +457,7 @@ class ProfileControllerTest { | ||||||
|   void testSetProfileWithoutAvatarUpload() throws InvalidInputException { |   void testSetProfileWithoutAvatarUpload() throws InvalidInputException { | ||||||
|     final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID)); |     final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID)); | ||||||
|     final byte[] name = TestRandomUtil.nextBytes(81); |     final byte[] name = TestRandomUtil.nextBytes(81); | ||||||
|  |     final byte[] phoneNumberSharing = TestRandomUtil.nextBytes(29); | ||||||
| 
 | 
 | ||||||
|     clearInvocations(AuthHelper.VALID_ACCOUNT_TWO); |     clearInvocations(AuthHelper.VALID_ACCOUNT_TWO); | ||||||
| 
 | 
 | ||||||
|  | @ -464,7 +466,7 @@ class ProfileControllerTest { | ||||||
|         .request() |         .request() | ||||||
|         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) |         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) | ||||||
|         .put(Entity.entity(new CreateProfileRequest(commitment, "anotherversion", name, null, null, |         .put(Entity.entity(new CreateProfileRequest(commitment, "anotherversion", name, null, null, | ||||||
|             null, false, false, List.of()), MediaType.APPLICATION_JSON_TYPE))) { |             null, false, false, Optional.of(List.of()), phoneNumberSharing), MediaType.APPLICATION_JSON_TYPE))) { | ||||||
| 
 | 
 | ||||||
|       assertThat(response.getStatus()).isEqualTo(200); |       assertThat(response.getStatus()).isEqualTo(200); | ||||||
|       assertThat(response.hasEntity()).isFalse(); |       assertThat(response.hasEntity()).isFalse(); | ||||||
|  | @ -482,6 +484,7 @@ class ProfileControllerTest { | ||||||
|       assertThat(profileArgumentCaptor.getValue().name()).isEqualTo(name); |       assertThat(profileArgumentCaptor.getValue().name()).isEqualTo(name); | ||||||
|       assertThat(profileArgumentCaptor.getValue().aboutEmoji()).isNull(); |       assertThat(profileArgumentCaptor.getValue().aboutEmoji()).isNull(); | ||||||
|       assertThat(profileArgumentCaptor.getValue().about()).isNull(); |       assertThat(profileArgumentCaptor.getValue().about()).isNull(); | ||||||
|  |       assertThat(profileArgumentCaptor.getValue().phoneNumberSharing()).isEqualTo(phoneNumberSharing); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -496,7 +499,7 @@ class ProfileControllerTest { | ||||||
|         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) |         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) | ||||||
|         .put(Entity.entity(new CreateProfileRequest(commitment, "validversion", |         .put(Entity.entity(new CreateProfileRequest(commitment, "validversion", | ||||||
|             name, null, null, |             name, null, null, | ||||||
|             null, true, false, List.of()), MediaType.APPLICATION_JSON_TYPE), ProfileAvatarUploadAttributes.class); |             null, true, false, Optional.of(List.of()), null), MediaType.APPLICATION_JSON_TYPE), ProfileAvatarUploadAttributes.class); | ||||||
| 
 | 
 | ||||||
|     final ArgumentCaptor<VersionedProfile> profileArgumentCaptor = ArgumentCaptor.forClass(VersionedProfile.class); |     final ArgumentCaptor<VersionedProfile> profileArgumentCaptor = ArgumentCaptor.forClass(VersionedProfile.class); | ||||||
| 
 | 
 | ||||||
|  | @ -522,7 +525,7 @@ class ProfileControllerTest { | ||||||
|         .request() |         .request() | ||||||
|         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) |         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) | ||||||
|         .put(Entity.entity(new CreateProfileRequest(commitment, "validversion", name, |         .put(Entity.entity(new CreateProfileRequest(commitment, "validversion", name, | ||||||
|             null, null, null, false, false, List.of()), MediaType.APPLICATION_JSON_TYPE))) { |             null, null, null, false, false, Optional.of(List.of()), null), MediaType.APPLICATION_JSON_TYPE))) { | ||||||
| 
 | 
 | ||||||
|       assertThat(response.getStatus()).isEqualTo(200); |       assertThat(response.getStatus()).isEqualTo(200); | ||||||
|       assertThat(response.hasEntity()).isFalse(); |       assertThat(response.hasEntity()).isFalse(); | ||||||
|  | @ -552,7 +555,7 @@ class ProfileControllerTest { | ||||||
|         .request() |         .request() | ||||||
|         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) |         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) | ||||||
|         .put(Entity.entity(new CreateProfileRequest(commitment, "validversion", name, |         .put(Entity.entity(new CreateProfileRequest(commitment, "validversion", name, | ||||||
|             null, null, null, true, true, List.of()), MediaType.APPLICATION_JSON_TYPE))) { |             null, null, null, true, true, Optional.of(List.of()), null), MediaType.APPLICATION_JSON_TYPE))) { | ||||||
| 
 | 
 | ||||||
|       assertThat(response.getStatus()).isEqualTo(200); |       assertThat(response.getStatus()).isEqualTo(200); | ||||||
|       assertThat(response.hasEntity()).isFalse(); |       assertThat(response.hasEntity()).isFalse(); | ||||||
|  | @ -583,7 +586,7 @@ class ProfileControllerTest { | ||||||
|         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) |         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) | ||||||
|         .put(Entity.entity(new CreateProfileRequest(commitment, "validversion", name, |         .put(Entity.entity(new CreateProfileRequest(commitment, "validversion", name, | ||||||
|             null, null, |             null, null, | ||||||
|             null, false, true, List.of()), MediaType.APPLICATION_JSON_TYPE))) { |             null, false, true, Optional.of(List.of()), null), MediaType.APPLICATION_JSON_TYPE))) { | ||||||
| 
 | 
 | ||||||
|       final ArgumentCaptor<VersionedProfile> profileArgumentCaptor = ArgumentCaptor.forClass(VersionedProfile.class); |       final ArgumentCaptor<VersionedProfile> profileArgumentCaptor = ArgumentCaptor.forClass(VersionedProfile.class); | ||||||
| 
 | 
 | ||||||
|  | @ -610,7 +613,7 @@ class ProfileControllerTest { | ||||||
|         .request() |         .request() | ||||||
|         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) |         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) | ||||||
|         .put(Entity.entity(new CreateProfileRequest(commitment, "validversion", name, |         .put(Entity.entity(new CreateProfileRequest(commitment, "validversion", name, | ||||||
|             null, null, null, true, true, List.of()), MediaType.APPLICATION_JSON_TYPE))) { |             null, null, null, true, true, Optional.of(List.of()), null), MediaType.APPLICATION_JSON_TYPE))) { | ||||||
| 
 | 
 | ||||||
|       assertThat(response.getStatus()).isEqualTo(200); |       assertThat(response.getStatus()).isEqualTo(200); | ||||||
|       assertThat(response.hasEntity()).isFalse(); |       assertThat(response.hasEntity()).isFalse(); | ||||||
|  | @ -642,7 +645,7 @@ class ProfileControllerTest { | ||||||
|         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) |         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) | ||||||
|         .put(Entity.entity( |         .put(Entity.entity( | ||||||
|             new CreateProfileRequest(commitment, "validversion", name, |             new CreateProfileRequest(commitment, "validversion", name, | ||||||
|                 null, null, null, true, false, List.of()), |                 null, null, null, true, false, Optional.of(List.of()), null), | ||||||
|             MediaType.APPLICATION_JSON_TYPE), ProfileAvatarUploadAttributes.class); |             MediaType.APPLICATION_JSON_TYPE), ProfileAvatarUploadAttributes.class); | ||||||
| 
 | 
 | ||||||
|     final ArgumentCaptor<VersionedProfile> profileArgumentCaptor = ArgumentCaptor.forClass(VersionedProfile.class); |     final ArgumentCaptor<VersionedProfile> profileArgumentCaptor = ArgumentCaptor.forClass(VersionedProfile.class); | ||||||
|  | @ -675,7 +678,7 @@ class ProfileControllerTest { | ||||||
|         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) |         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) | ||||||
|         .put(Entity.entity( |         .put(Entity.entity( | ||||||
|             new CreateProfileRequest(commitment, "anotherversion", name, emoji, about, null, |             new CreateProfileRequest(commitment, "anotherversion", name, emoji, about, null, | ||||||
|                 false, false, List.of()), |                 false, false, Optional.of(List.of()), null), | ||||||
|             MediaType.APPLICATION_JSON_TYPE))) { |             MediaType.APPLICATION_JSON_TYPE))) { | ||||||
| 
 | 
 | ||||||
|       assertThat(response.getStatus()).isEqualTo(200); |       assertThat(response.getStatus()).isEqualTo(200); | ||||||
|  | @ -715,7 +718,7 @@ class ProfileControllerTest { | ||||||
|         .put(Entity.entity( |         .put(Entity.entity( | ||||||
|             new CreateProfileRequest(commitment, "yetanotherversion", name, |             new CreateProfileRequest(commitment, "yetanotherversion", name, | ||||||
|                 null, null, paymentAddress, false, false, |                 null, null, paymentAddress, false, false, | ||||||
|                 List.of()), MediaType.APPLICATION_JSON_TYPE))) { |                 Optional.of(List.of()), null), MediaType.APPLICATION_JSON_TYPE))) { | ||||||
| 
 | 
 | ||||||
|       assertThat(response.getStatus()).isEqualTo(200); |       assertThat(response.getStatus()).isEqualTo(200); | ||||||
|       assertThat(response.hasEntity()).isFalse(); |       assertThat(response.hasEntity()).isFalse(); | ||||||
|  | @ -757,7 +760,7 @@ class ProfileControllerTest { | ||||||
|         .put(Entity.entity( |         .put(Entity.entity( | ||||||
|             new CreateProfileRequest(commitment, "yetanotherversion", name, |             new CreateProfileRequest(commitment, "yetanotherversion", name, | ||||||
|                 null, null, paymentAddress, false, false, |                 null, null, paymentAddress, false, false, | ||||||
|                 List.of()), MediaType.APPLICATION_JSON_TYPE))) { |                 Optional.of(List.of()), null), MediaType.APPLICATION_JSON_TYPE))) { | ||||||
| 
 | 
 | ||||||
|       assertThat(response.getStatus()).isEqualTo(403); |       assertThat(response.getStatus()).isEqualTo(403); | ||||||
|       assertThat(response.hasEntity()).isFalse(); |       assertThat(response.hasEntity()).isFalse(); | ||||||
|  | @ -776,6 +779,7 @@ class ProfileControllerTest { | ||||||
|     final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID)); |     final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID)); | ||||||
|     final byte[] name = TestRandomUtil.nextBytes(81); |     final byte[] name = TestRandomUtil.nextBytes(81); | ||||||
|     final byte[] paymentAddress = TestRandomUtil.nextBytes(582); |     final byte[] paymentAddress = TestRandomUtil.nextBytes(582); | ||||||
|  |     final byte[] phoneNumberSharing = TestRandomUtil.nextBytes(29); | ||||||
| 
 | 
 | ||||||
|     clearInvocations(AuthHelper.VALID_ACCOUNT_TWO); |     clearInvocations(AuthHelper.VALID_ACCOUNT_TWO); | ||||||
| 
 | 
 | ||||||
|  | @ -783,6 +787,7 @@ class ProfileControllerTest { | ||||||
|         .thenReturn(Optional.of( |         .thenReturn(Optional.of( | ||||||
|             new VersionedProfile("1", name, null, null, null, |             new VersionedProfile("1", name, null, null, null, | ||||||
|                 existingPaymentAddressOnProfile ? TestRandomUtil.nextBytes(582) : null, |                 existingPaymentAddressOnProfile ? TestRandomUtil.nextBytes(582) : null, | ||||||
|  |                 phoneNumberSharing, | ||||||
|                 commitment.serialize()))); |                 commitment.serialize()))); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @ -793,7 +798,7 @@ class ProfileControllerTest { | ||||||
|         .put(Entity.entity( |         .put(Entity.entity( | ||||||
|             new CreateProfileRequest(commitment, "yetanotherversion", name, |             new CreateProfileRequest(commitment, "yetanotherversion", name, | ||||||
|                 null, null, paymentAddress, false, false, |                 null, null, paymentAddress, false, false, | ||||||
|                 List.of()), MediaType.APPLICATION_JSON_TYPE))) { |                 Optional.of(List.of()), null), MediaType.APPLICATION_JSON_TYPE))) { | ||||||
| 
 | 
 | ||||||
|       if (existingPaymentAddressOnProfile) { |       if (existingPaymentAddressOnProfile) { | ||||||
|         assertThat(response.getStatus()).isEqualTo(200); |         assertThat(response.getStatus()).isEqualTo(200); | ||||||
|  | @ -823,14 +828,49 @@ class ProfileControllerTest { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   @Test | ||||||
|  |   void testSetProfilePhoneNumberSharing() throws Exception { | ||||||
|  |     final ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(new ServiceId.Aci(AuthHelper.VALID_UUID)); | ||||||
|  |     final byte[] name = TestRandomUtil.nextBytes(81); | ||||||
|  |     final byte[] phoneNumberSharing = TestRandomUtil.nextBytes(29); | ||||||
|  | 
 | ||||||
|  |     clearInvocations(AuthHelper.VALID_ACCOUNT_TWO); | ||||||
|  | 
 | ||||||
|  |     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, "anotherversion", name, null, null, | ||||||
|  |             null, false, false, Optional.of(List.of()), phoneNumberSharing), MediaType.APPLICATION_JSON_TYPE))) { | ||||||
|  | 
 | ||||||
|  |       assertThat(response.getStatus()).isEqualTo(200); | ||||||
|  |       assertThat(response.hasEntity()).isFalse(); | ||||||
|  | 
 | ||||||
|  |       final 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().commitment()).isEqualTo(commitment.serialize()); | ||||||
|  |       assertThat(profileArgumentCaptor.getValue().avatar()).isNull(); | ||||||
|  |       assertThat(profileArgumentCaptor.getValue().version()).isEqualTo("anotherversion"); | ||||||
|  |       assertThat(profileArgumentCaptor.getValue().name()).isEqualTo(name); | ||||||
|  |       assertThat(profileArgumentCaptor.getValue().aboutEmoji()).isNull(); | ||||||
|  |       assertThat(profileArgumentCaptor.getValue().about()).isNull(); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   @Test |   @Test | ||||||
|   void testGetProfileByVersion() throws RateLimitExceededException { |   void testGetProfileByVersion() throws RateLimitExceededException { | ||||||
|     final byte[] name = TestRandomUtil.nextBytes(81); |     final byte[] name = TestRandomUtil.nextBytes(81); | ||||||
|     final byte[] emoji = TestRandomUtil.nextBytes(60); |     final byte[] emoji = TestRandomUtil.nextBytes(60); | ||||||
|     final byte[] about = TestRandomUtil.nextBytes(156); |     final byte[] about = TestRandomUtil.nextBytes(156); | ||||||
|  |     final byte[] phoneNumberSharing = TestRandomUtil.nextBytes(29); | ||||||
| 
 | 
 | ||||||
|     when(profilesManager.get(eq(AuthHelper.VALID_UUID_TWO), eq("validversion"))).thenReturn(Optional.of(new VersionedProfile( |     when(profilesManager.get(eq(AuthHelper.VALID_UUID_TWO), eq("validversion"))).thenReturn(Optional.of(new VersionedProfile( | ||||||
|         "validversion", name, "profiles/validavatar", emoji, about, null, "validcommitmnet".getBytes()))); |         "validversion", name, "profiles/validavatar", emoji, about, null, phoneNumberSharing, "validcommitment".getBytes()))); | ||||||
| 
 | 
 | ||||||
|     final VersionedProfileResponse profile = resources.getJerseyTest() |     final VersionedProfileResponse profile = resources.getJerseyTest() | ||||||
|         .target("/v1/profile/" + AuthHelper.VALID_UUID_TWO + "/validversion") |         .target("/v1/profile/" + AuthHelper.VALID_UUID_TWO + "/validversion") | ||||||
|  | @ -843,6 +883,7 @@ class ProfileControllerTest { | ||||||
|     assertThat(profile.getAbout()).containsExactly(about); |     assertThat(profile.getAbout()).containsExactly(about); | ||||||
|     assertThat(profile.getAboutEmoji()).containsExactly(emoji); |     assertThat(profile.getAboutEmoji()).containsExactly(emoji); | ||||||
|     assertThat(profile.getAvatar()).isEqualTo("profiles/validavatar"); |     assertThat(profile.getAvatar()).isEqualTo("profiles/validavatar"); | ||||||
|  |     assertThat(profile.getPhoneNumberSharing()).containsExactly(phoneNumberSharing); | ||||||
|     assertThat(profile.getBaseProfileResponse().getCapabilities().gv1Migration()).isTrue(); |     assertThat(profile.getBaseProfileResponse().getCapabilities().gv1Migration()).isTrue(); | ||||||
|     assertThat(profile.getBaseProfileResponse().getUuid()).isEqualTo(new AciServiceIdentifier(AuthHelper.VALID_UUID_TWO)); |     assertThat(profile.getBaseProfileResponse().getUuid()).isEqualTo(new AciServiceIdentifier(AuthHelper.VALID_UUID_TWO)); | ||||||
|     assertThat(profile.getBaseProfileResponse().getBadges()).hasSize(1).element(0).has(new Condition<>( |     assertThat(profile.getBaseProfileResponse().getBadges()).hasSize(1).element(0).has(new Condition<>( | ||||||
|  | @ -869,7 +910,7 @@ class ProfileControllerTest { | ||||||
|         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) |         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) | ||||||
|         .put(Entity.entity( |         .put(Entity.entity( | ||||||
|             new CreateProfileRequest(commitment, "someversion", name, null, null, paymentAddress, false, false, |             new CreateProfileRequest(commitment, "someversion", name, null, null, paymentAddress, false, false, | ||||||
|                 List.of()), MediaType.APPLICATION_JSON_TYPE))) { |                 Optional.of(List.of()), null), MediaType.APPLICATION_JSON_TYPE))) { | ||||||
| 
 | 
 | ||||||
|       assertThat(response.getStatus()).isEqualTo(200); |       assertThat(response.getStatus()).isEqualTo(200); | ||||||
|       assertThat(response.hasEntity()).isFalse(); |       assertThat(response.hasEntity()).isFalse(); | ||||||
|  | @ -882,7 +923,7 @@ class ProfileControllerTest { | ||||||
|   void testGetProfileReturnsNoPaymentAddressIfCurrentVersionMismatch() { |   void testGetProfileReturnsNoPaymentAddressIfCurrentVersionMismatch() { | ||||||
|     final byte[] paymentAddress = TestRandomUtil.nextBytes(582); |     final byte[] paymentAddress = TestRandomUtil.nextBytes(582); | ||||||
|     when(profilesManager.get(AuthHelper.VALID_UUID_TWO, "validversion")).thenReturn( |     when(profilesManager.get(AuthHelper.VALID_UUID_TWO, "validversion")).thenReturn( | ||||||
|         Optional.of(new VersionedProfile(null, null, null, null, null, paymentAddress, null))); |         Optional.of(new VersionedProfile(null, null, null, null, null, paymentAddress, null, null))); | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|       final VersionedProfileResponse profile = resources.getJerseyTest() |       final VersionedProfileResponse profile = resources.getJerseyTest() | ||||||
|  | @ -959,7 +1000,7 @@ class ProfileControllerTest { | ||||||
|         .request() |         .request() | ||||||
|         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) |         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) | ||||||
|         .put(Entity.entity(new CreateProfileRequest(commitment, "anotherversion", name, emoji, about, null, false, false, |         .put(Entity.entity(new CreateProfileRequest(commitment, "anotherversion", name, emoji, about, null, false, false, | ||||||
|             List.of("TEST2")), MediaType.APPLICATION_JSON_TYPE))) { |             Optional.of(List.of("TEST2")), null), MediaType.APPLICATION_JSON_TYPE))) { | ||||||
| 
 | 
 | ||||||
|       assertThat(response.getStatus()).isEqualTo(200); |       assertThat(response.getStatus()).isEqualTo(200); | ||||||
|       assertThat(response.hasEntity()).isFalse(); |       assertThat(response.hasEntity()).isFalse(); | ||||||
|  | @ -982,7 +1023,7 @@ class ProfileControllerTest { | ||||||
|         .request() |         .request() | ||||||
|         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) |         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) | ||||||
|         .put(Entity.entity(new CreateProfileRequest(commitment, "anotherversion", name, emoji, about, null, false, false, |         .put(Entity.entity(new CreateProfileRequest(commitment, "anotherversion", name, emoji, about, null, false, false, | ||||||
|             List.of("TEST3", "TEST2")), MediaType.APPLICATION_JSON_TYPE))) { |             Optional.of(List.of("TEST3", "TEST2")), null), MediaType.APPLICATION_JSON_TYPE))) { | ||||||
| 
 | 
 | ||||||
|       assertThat(response.getStatus()).isEqualTo(200); |       assertThat(response.getStatus()).isEqualTo(200); | ||||||
|       assertThat(response.hasEntity()).isFalse(); |       assertThat(response.hasEntity()).isFalse(); | ||||||
|  | @ -1008,7 +1049,7 @@ class ProfileControllerTest { | ||||||
|         .request() |         .request() | ||||||
|         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) |         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) | ||||||
|         .put(Entity.entity(new CreateProfileRequest(commitment, "anotherversion", name, emoji, about, null, false, false, |         .put(Entity.entity(new CreateProfileRequest(commitment, "anotherversion", name, emoji, about, null, false, false, | ||||||
|             List.of("TEST2", "TEST3")), MediaType.APPLICATION_JSON_TYPE))) { |             Optional.of(List.of("TEST2", "TEST3")), null), MediaType.APPLICATION_JSON_TYPE))) { | ||||||
| 
 | 
 | ||||||
|       assertThat(response.getStatus()).isEqualTo(200); |       assertThat(response.getStatus()).isEqualTo(200); | ||||||
|       assertThat(response.hasEntity()).isFalse(); |       assertThat(response.hasEntity()).isFalse(); | ||||||
|  | @ -1034,7 +1075,7 @@ class ProfileControllerTest { | ||||||
|         .request() |         .request() | ||||||
|         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) |         .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_TWO, AuthHelper.VALID_PASSWORD_TWO)) | ||||||
|         .put(Entity.entity(new CreateProfileRequest(commitment, "anotherversion", name, emoji, about, null, false, false, |         .put(Entity.entity(new CreateProfileRequest(commitment, "anotherversion", name, emoji, about, null, false, false, | ||||||
|             List.of("TEST1")), MediaType.APPLICATION_JSON_TYPE))) { |             Optional.of(List.of("TEST1")), null), MediaType.APPLICATION_JSON_TYPE))) { | ||||||
| 
 | 
 | ||||||
|       assertThat(response.getStatus()).isEqualTo(200); |       assertThat(response.getStatus()).isEqualTo(200); | ||||||
|       assertThat(response.hasEntity()).isFalse(); |       assertThat(response.hasEntity()).isFalse(); | ||||||
|  |  | ||||||
|  | @ -218,6 +218,7 @@ public class ProfileGrpcServiceTest extends SimpleBaseGrpcTest<ProfileGrpcServic | ||||||
|     final byte[] validAboutEmoji = new byte[60]; |     final byte[] validAboutEmoji = new byte[60]; | ||||||
|     final byte[] validAbout = new byte[540]; |     final byte[] validAbout = new byte[540]; | ||||||
|     final byte[] validPaymentAddress = new byte[582]; |     final byte[] validPaymentAddress = new byte[582]; | ||||||
|  |     final byte[] validPhoneNumberSharing = new byte[29]; | ||||||
| 
 | 
 | ||||||
|     final SetProfileRequest request = SetProfileRequest.newBuilder() |     final SetProfileRequest request = SetProfileRequest.newBuilder() | ||||||
|         .setVersion(VERSION) |         .setVersion(VERSION) | ||||||
|  | @ -226,6 +227,7 @@ public class ProfileGrpcServiceTest extends SimpleBaseGrpcTest<ProfileGrpcServic | ||||||
|         .setAboutEmoji(ByteString.copyFrom(validAboutEmoji)) |         .setAboutEmoji(ByteString.copyFrom(validAboutEmoji)) | ||||||
|         .setAbout(ByteString.copyFrom(validAbout)) |         .setAbout(ByteString.copyFrom(validAbout)) | ||||||
|         .setPaymentAddress(ByteString.copyFrom(validPaymentAddress)) |         .setPaymentAddress(ByteString.copyFrom(validPaymentAddress)) | ||||||
|  |         .setPhoneNumberSharing(ByteString.copyFrom(validPhoneNumberSharing)) | ||||||
|         .setCommitment(ByteString.copyFrom(commitment)) |         .setCommitment(ByteString.copyFrom(commitment)) | ||||||
|         .build(); |         .build(); | ||||||
| 
 | 
 | ||||||
|  | @ -244,6 +246,7 @@ public class ProfileGrpcServiceTest extends SimpleBaseGrpcTest<ProfileGrpcServic | ||||||
|     assertThat(profile.aboutEmoji()).isEqualTo(validAboutEmoji); |     assertThat(profile.aboutEmoji()).isEqualTo(validAboutEmoji); | ||||||
|     assertThat(profile.about()).isEqualTo(validAbout); |     assertThat(profile.about()).isEqualTo(validAbout); | ||||||
|     assertThat(profile.paymentAddress()).isEqualTo(validPaymentAddress); |     assertThat(profile.paymentAddress()).isEqualTo(validPaymentAddress); | ||||||
|  |     assertThat(profile.phoneNumberSharing()).isEqualTo(validPhoneNumberSharing); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @ParameterizedTest |   @ParameterizedTest | ||||||
|  |  | ||||||
|  | @ -110,7 +110,7 @@ public class ProfilesManagerTest { | ||||||
|     final UUID uuid = UUID.randomUUID(); |     final UUID uuid = UUID.randomUUID(); | ||||||
|     final byte[] name = TestRandomUtil.nextBytes(81); |     final byte[] name = TestRandomUtil.nextBytes(81); | ||||||
|     final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null, |     final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null, | ||||||
|         null, "somecommitment".getBytes()); |         null, null, "somecommitment".getBytes()); | ||||||
| 
 | 
 | ||||||
|     when(commands.hget(eq("profiles::" + uuid), eq("someversion"))).thenReturn(null); |     when(commands.hget(eq("profiles::" + uuid), eq("someversion"))).thenReturn(null); | ||||||
|     when(profiles.get(eq(uuid), eq("someversion"))).thenReturn(Optional.of(profile)); |     when(profiles.get(eq(uuid), eq("someversion"))).thenReturn(Optional.of(profile)); | ||||||
|  | @ -133,7 +133,7 @@ public class ProfilesManagerTest { | ||||||
|     final UUID uuid = UUID.randomUUID(); |     final UUID uuid = UUID.randomUUID(); | ||||||
|     final byte[] name = TestRandomUtil.nextBytes(81); |     final byte[] name = TestRandomUtil.nextBytes(81); | ||||||
|     final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null, |     final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null, | ||||||
|         null, "somecommitment".getBytes()); |         null, null, "somecommitment".getBytes()); | ||||||
| 
 | 
 | ||||||
|     when(asyncCommands.hget(eq("profiles::" + uuid), eq("someversion"))).thenReturn(MockRedisFuture.completedFuture(null)); |     when(asyncCommands.hget(eq("profiles::" + uuid), eq("someversion"))).thenReturn(MockRedisFuture.completedFuture(null)); | ||||||
|     when(asyncCommands.hset(eq("profiles::" + uuid), eq("someversion"), anyString())).thenReturn(MockRedisFuture.completedFuture(null)); |     when(asyncCommands.hset(eq("profiles::" + uuid), eq("someversion"), anyString())).thenReturn(MockRedisFuture.completedFuture(null)); | ||||||
|  | @ -157,7 +157,7 @@ public class ProfilesManagerTest { | ||||||
|     final UUID uuid = UUID.randomUUID(); |     final UUID uuid = UUID.randomUUID(); | ||||||
|     final byte[] name = TestRandomUtil.nextBytes(81); |     final byte[] name = TestRandomUtil.nextBytes(81); | ||||||
|     final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null, |     final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null, | ||||||
|         null, "somecommitment".getBytes()); |         null, null, "somecommitment".getBytes()); | ||||||
| 
 | 
 | ||||||
|     when(commands.hget(eq("profiles::" + uuid), eq("someversion"))).thenThrow(new RedisException("Connection lost")); |     when(commands.hget(eq("profiles::" + uuid), eq("someversion"))).thenThrow(new RedisException("Connection lost")); | ||||||
|     when(profiles.get(eq(uuid), eq("someversion"))).thenReturn(Optional.of(profile)); |     when(profiles.get(eq(uuid), eq("someversion"))).thenReturn(Optional.of(profile)); | ||||||
|  | @ -180,7 +180,7 @@ public class ProfilesManagerTest { | ||||||
|     final UUID uuid = UUID.randomUUID(); |     final UUID uuid = UUID.randomUUID(); | ||||||
|     final byte[] name = TestRandomUtil.nextBytes(81); |     final byte[] name = TestRandomUtil.nextBytes(81); | ||||||
|     final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null, |     final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null, | ||||||
|         null, "somecommitment".getBytes()); |         null, null, "somecommitment".getBytes()); | ||||||
| 
 | 
 | ||||||
|     when(asyncCommands.hget(eq("profiles::" + uuid), eq("someversion"))).thenReturn(MockRedisFuture.failedFuture(new RedisException("Connection lost"))); |     when(asyncCommands.hget(eq("profiles::" + uuid), eq("someversion"))).thenReturn(MockRedisFuture.failedFuture(new RedisException("Connection lost"))); | ||||||
|     when(asyncCommands.hset(eq("profiles::" + uuid), eq("someversion"), anyString())).thenReturn(MockRedisFuture.completedFuture(null)); |     when(asyncCommands.hset(eq("profiles::" + uuid), eq("someversion"), anyString())).thenReturn(MockRedisFuture.completedFuture(null)); | ||||||
|  | @ -204,7 +204,7 @@ public class ProfilesManagerTest { | ||||||
|     final UUID uuid = UUID.randomUUID(); |     final UUID uuid = UUID.randomUUID(); | ||||||
|     final byte[] name = TestRandomUtil.nextBytes(81); |     final byte[] name = TestRandomUtil.nextBytes(81); | ||||||
|     final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null, |     final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null, | ||||||
|         null, "somecommitment".getBytes()); |         null, null, "somecommitment".getBytes()); | ||||||
| 
 | 
 | ||||||
|     profilesManager.set(uuid, profile); |     profilesManager.set(uuid, profile); | ||||||
| 
 | 
 | ||||||
|  | @ -220,7 +220,7 @@ public class ProfilesManagerTest { | ||||||
|     final UUID uuid = UUID.randomUUID(); |     final UUID uuid = UUID.randomUUID(); | ||||||
|     final byte[] name = TestRandomUtil.nextBytes(81); |     final byte[] name = TestRandomUtil.nextBytes(81); | ||||||
|     final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null, |     final VersionedProfile profile = new VersionedProfile("someversion", name, "someavatar", null, null, | ||||||
|         null, "somecommitment".getBytes()); |         null, null, "somecommitment".getBytes()); | ||||||
| 
 | 
 | ||||||
|     when(asyncCommands.hset(eq("profiles::" + uuid), eq("someversion"), anyString())).thenReturn(MockRedisFuture.completedFuture(null)); |     when(asyncCommands.hset(eq("profiles::" + uuid), eq("someversion"), anyString())).thenReturn(MockRedisFuture.completedFuture(null)); | ||||||
|     when(profiles.setAsync(eq(uuid), eq(profile))).thenReturn(CompletableFuture.completedFuture(null)); |     when(profiles.setAsync(eq(uuid), eq(profile))).thenReturn(CompletableFuture.completedFuture(null)); | ||||||
|  |  | ||||||
|  | @ -48,8 +48,9 @@ public class ProfilesTest { | ||||||
|     final byte[] validAboutEmoji = TestRandomUtil.nextBytes(60); |     final byte[] validAboutEmoji = TestRandomUtil.nextBytes(60); | ||||||
|     final byte[] validAbout = TestRandomUtil.nextBytes(156); |     final byte[] validAbout = TestRandomUtil.nextBytes(156); | ||||||
|     final String avatar = "profiles/" + ProfileTestHelper.generateRandomBase64FromByteArray(16); |     final String avatar = "profiles/" + ProfileTestHelper.generateRandomBase64FromByteArray(16); | ||||||
|  |     final byte[] phoneNumberSharing = TestRandomUtil.nextBytes(29); | ||||||
| 
 | 
 | ||||||
|     validProfile = new VersionedProfile(version, name, avatar, validAboutEmoji, validAbout, null, commitment); |     validProfile = new VersionedProfile(version, name, avatar, validAboutEmoji, validAbout, null, phoneNumberSharing, commitment); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Test |   @Test | ||||||
|  | @ -92,10 +93,11 @@ public class ProfilesTest { | ||||||
|     final byte[] differentEmoji = TestRandomUtil.nextBytes(60); |     final byte[] differentEmoji = TestRandomUtil.nextBytes(60); | ||||||
|     final byte[] differentAbout = TestRandomUtil.nextBytes(156); |     final byte[] differentAbout = TestRandomUtil.nextBytes(156); | ||||||
|     final byte[] paymentAddress = TestRandomUtil.nextBytes(582); |     final byte[] paymentAddress = TestRandomUtil.nextBytes(582); | ||||||
|  |     final byte[] phoneNumberSharing = TestRandomUtil.nextBytes(29); | ||||||
|     final byte[] commitment = new ProfileKey(TestRandomUtil.nextBytes(32)).getCommitment(new ServiceId.Aci(ACI)).serialize(); |     final byte[] commitment = new ProfileKey(TestRandomUtil.nextBytes(32)).getCommitment(new ServiceId.Aci(ACI)).serialize(); | ||||||
| 
 | 
 | ||||||
|     VersionedProfile updatedProfile = new VersionedProfile(version, name, differentAvatar, |     VersionedProfile updatedProfile = new VersionedProfile(version, name, differentAvatar, | ||||||
|         differentEmoji, differentAbout, paymentAddress, commitment); |         differentEmoji, differentAbout, paymentAddress, phoneNumberSharing, commitment); | ||||||
| 
 | 
 | ||||||
|     profiles.set(ACI, updatedProfile); |     profiles.set(ACI, updatedProfile); | ||||||
| 
 | 
 | ||||||
|  | @ -115,7 +117,7 @@ public class ProfilesTest { | ||||||
|     final byte[] name = TestRandomUtil.nextBytes(81); |     final byte[] name = TestRandomUtil.nextBytes(81); | ||||||
|     final byte[] commitment = new ProfileKey(TestRandomUtil.nextBytes(32)).getCommitment(new ServiceId.Aci(ACI)).serialize(); |     final byte[] commitment = new ProfileKey(TestRandomUtil.nextBytes(32)).getCommitment(new ServiceId.Aci(ACI)).serialize(); | ||||||
| 
 | 
 | ||||||
|     VersionedProfile profile = new VersionedProfile(version, name, null, null, null, null, |     VersionedProfile profile = new VersionedProfile(version, name, null, null, null, null, null, | ||||||
|         commitment); |         commitment); | ||||||
|     profiles.set(ACI, profile); |     profiles.set(ACI, profile); | ||||||
| 
 | 
 | ||||||
|  | @ -147,10 +149,11 @@ public class ProfilesTest { | ||||||
|     final byte[] differentEmoji = TestRandomUtil.nextBytes(60); |     final byte[] differentEmoji = TestRandomUtil.nextBytes(60); | ||||||
|     final byte[] differentAbout = TestRandomUtil.nextBytes(156); |     final byte[] differentAbout = TestRandomUtil.nextBytes(156); | ||||||
|     final String differentAvatar = "profiles/" + ProfileTestHelper.generateRandomBase64FromByteArray(16); |     final String differentAvatar = "profiles/" + ProfileTestHelper.generateRandomBase64FromByteArray(16); | ||||||
|  |     final byte[] differentPhoneNumberSharing = TestRandomUtil.nextBytes(29); | ||||||
|     final byte[] differentCommitment = new ProfileKey(TestRandomUtil.nextBytes(32)).getCommitment(new ServiceId.Aci(ACI)).serialize(); |     final byte[] differentCommitment = new ProfileKey(TestRandomUtil.nextBytes(32)).getCommitment(new ServiceId.Aci(ACI)).serialize(); | ||||||
| 
 | 
 | ||||||
|     VersionedProfile updated = new VersionedProfile(validProfile.version(), differentName, differentAvatar, differentEmoji, differentAbout, null, |     VersionedProfile updated = new VersionedProfile(validProfile.version(), differentName, differentAvatar, differentEmoji, differentAbout, null, | ||||||
|         differentCommitment); |         differentPhoneNumberSharing, differentCommitment); | ||||||
|     profiles.set(ACI, updated); |     profiles.set(ACI, updated); | ||||||
| 
 | 
 | ||||||
|     retrieved = profiles.get(ACI, updated.version()); |     retrieved = profiles.get(ACI, updated.version()); | ||||||
|  | @ -160,6 +163,7 @@ public class ProfilesTest { | ||||||
|     assertThat(retrieved.get().about()).isEqualTo(updated.about()); |     assertThat(retrieved.get().about()).isEqualTo(updated.about()); | ||||||
|     assertThat(retrieved.get().aboutEmoji()).isEqualTo(updated.aboutEmoji()); |     assertThat(retrieved.get().aboutEmoji()).isEqualTo(updated.aboutEmoji()); | ||||||
|     assertThat(retrieved.get().avatar()).isEqualTo(updated.avatar()); |     assertThat(retrieved.get().avatar()).isEqualTo(updated.avatar()); | ||||||
|  |     assertThat(retrieved.get().phoneNumberSharing()).isEqualTo(updated.phoneNumberSharing()); | ||||||
| 
 | 
 | ||||||
|     // Commitment should be unchanged after an overwrite |     // Commitment should be unchanged after an overwrite | ||||||
|     assertThat(retrieved.get().commitment()).isEqualTo(validProfile.commitment()); |     assertThat(retrieved.get().commitment()).isEqualTo(validProfile.commitment()); | ||||||
|  | @ -183,8 +187,8 @@ public class ProfilesTest { | ||||||
|     final byte[] commitmentTwo = new ProfileKey(TestRandomUtil.nextBytes(32)).getCommitment(new ServiceId.Aci(ACI)).serialize(); |     final byte[] commitmentTwo = new ProfileKey(TestRandomUtil.nextBytes(32)).getCommitment(new ServiceId.Aci(ACI)).serialize(); | ||||||
| 
 | 
 | ||||||
|     VersionedProfile profileOne = new VersionedProfile(versionOne, nameOne, avatarOne, null, null, |     VersionedProfile profileOne = new VersionedProfile(versionOne, nameOne, avatarOne, null, null, | ||||||
|         null, commitmentOne); |         null, null, commitmentOne); | ||||||
|     VersionedProfile profileTwo = new VersionedProfile(versionTwo, nameTwo, avatarTwo, aboutEmoji, about, null, commitmentTwo); |     VersionedProfile profileTwo = new VersionedProfile(versionTwo, nameTwo, avatarTwo, aboutEmoji, about, null, null, commitmentTwo); | ||||||
| 
 | 
 | ||||||
|     profiles.set(ACI, profileOne); |     profiles.set(ACI, profileOne); | ||||||
|     profiles.set(ACI, profileTwo); |     profiles.set(ACI, profileTwo); | ||||||
|  | @ -236,8 +240,8 @@ public class ProfilesTest { | ||||||
|     final byte[] commitmentTwo = new ProfileKey(TestRandomUtil.nextBytes(32)).getCommitment(new ServiceId.Aci(ACI)).serialize(); |     final byte[] commitmentTwo = new ProfileKey(TestRandomUtil.nextBytes(32)).getCommitment(new ServiceId.Aci(ACI)).serialize(); | ||||||
| 
 | 
 | ||||||
|     VersionedProfile profileOne = new VersionedProfile(versionOne, nameOne, avatarOne, null, null, |     VersionedProfile profileOne = new VersionedProfile(versionOne, nameOne, avatarOne, null, null, | ||||||
|         null, commitmentOne); |         null, null, commitmentOne); | ||||||
|     VersionedProfile profileTwo = new VersionedProfile(versionTwo, nameTwo, avatarTwo, aboutEmoji, about, null, commitmentTwo); |     VersionedProfile profileTwo = new VersionedProfile(versionTwo, nameTwo, avatarTwo, aboutEmoji, about, null, null, commitmentTwo); | ||||||
| 
 | 
 | ||||||
|     profiles.set(ACI, profileOne); |     profiles.set(ACI, profileOne); | ||||||
|     profiles.set(ACI, profileTwo); |     profiles.set(ACI, profileTwo); | ||||||
|  | @ -266,32 +270,37 @@ public class ProfilesTest { | ||||||
|     final byte[] emoji = TestRandomUtil.nextBytes(60); |     final byte[] emoji = TestRandomUtil.nextBytes(60); | ||||||
|     final byte[] about = TestRandomUtil.nextBytes(156); |     final byte[] about = TestRandomUtil.nextBytes(156); | ||||||
|     final byte[] paymentAddress = TestRandomUtil.nextBytes(582); |     final byte[] paymentAddress = TestRandomUtil.nextBytes(582); | ||||||
|  |     final byte[] phoneNumberSharing = TestRandomUtil.nextBytes(29); | ||||||
|     final byte[] commitment = new ProfileKey(TestRandomUtil.nextBytes(32)).getCommitment(new ServiceId.Aci(ACI)).serialize(); |     final byte[] commitment = new ProfileKey(TestRandomUtil.nextBytes(32)).getCommitment(new ServiceId.Aci(ACI)).serialize(); | ||||||
| 
 | 
 | ||||||
|     return Stream.of( |     return Stream.of( | ||||||
|         Arguments.of( |         Arguments.of( | ||||||
|             new VersionedProfile(version, name, avatar, emoji, about, paymentAddress, commitment), |             new VersionedProfile(version, name, avatar, emoji, about, paymentAddress, phoneNumberSharing, commitment), | ||||||
|             "SET #commitment = if_not_exists(#commitment, :commitment), #name = :name, #avatar = :avatar, #about = :about, #aboutEmoji = :aboutEmoji, #paymentAddress = :paymentAddress"), |             "SET #commitment = if_not_exists(#commitment, :commitment), #name = :name, #avatar = :avatar, #about = :about, #aboutEmoji = :aboutEmoji, #paymentAddress = :paymentAddress, #phoneNumberSharing = :phoneNumberSharing"), | ||||||
| 
 | 
 | ||||||
|         Arguments.of( |         Arguments.of( | ||||||
|             new VersionedProfile(version, name, avatar, emoji, about, null, commitment), |             new VersionedProfile(version, name, avatar, emoji, about, paymentAddress, null, commitment), | ||||||
|             "SET #commitment = if_not_exists(#commitment, :commitment), #name = :name, #avatar = :avatar, #about = :about, #aboutEmoji = :aboutEmoji REMOVE #paymentAddress"), |             "SET #commitment = if_not_exists(#commitment, :commitment), #name = :name, #avatar = :avatar, #about = :about, #aboutEmoji = :aboutEmoji, #paymentAddress = :paymentAddress REMOVE #phoneNumberSharing"), | ||||||
| 
 | 
 | ||||||
|         Arguments.of( |         Arguments.of( | ||||||
|             new VersionedProfile(version, name, avatar, emoji, null, null, commitment), |             new VersionedProfile(version, name, avatar, emoji, about, null, null, commitment), | ||||||
|             "SET #commitment = if_not_exists(#commitment, :commitment), #name = :name, #avatar = :avatar, #aboutEmoji = :aboutEmoji REMOVE #about, #paymentAddress"), |             "SET #commitment = if_not_exists(#commitment, :commitment), #name = :name, #avatar = :avatar, #about = :about, #aboutEmoji = :aboutEmoji REMOVE #paymentAddress, #phoneNumberSharing"), | ||||||
| 
 | 
 | ||||||
|         Arguments.of( |         Arguments.of( | ||||||
|             new VersionedProfile(version, name, avatar, null, null, null, commitment), |             new VersionedProfile(version, name, avatar, emoji, null, null, null, commitment), | ||||||
|             "SET #commitment = if_not_exists(#commitment, :commitment), #name = :name, #avatar = :avatar REMOVE #about, #aboutEmoji, #paymentAddress"), |             "SET #commitment = if_not_exists(#commitment, :commitment), #name = :name, #avatar = :avatar, #aboutEmoji = :aboutEmoji REMOVE #about, #paymentAddress, #phoneNumberSharing"), | ||||||
| 
 | 
 | ||||||
|         Arguments.of( |         Arguments.of( | ||||||
|             new VersionedProfile(version, name, null, null, null, null, commitment), |             new VersionedProfile(version, name, avatar, null, null, null, null, commitment), | ||||||
|             "SET #commitment = if_not_exists(#commitment, :commitment), #name = :name REMOVE #avatar, #about, #aboutEmoji, #paymentAddress"), |             "SET #commitment = if_not_exists(#commitment, :commitment), #name = :name, #avatar = :avatar REMOVE #about, #aboutEmoji, #paymentAddress, #phoneNumberSharing"), | ||||||
| 
 | 
 | ||||||
|         Arguments.of( |         Arguments.of( | ||||||
|             new VersionedProfile(version, null, null, null, null, null, commitment), |             new VersionedProfile(version, name, null, null, null, null, null, commitment), | ||||||
|             "SET #commitment = if_not_exists(#commitment, :commitment) REMOVE #name, #avatar, #about, #aboutEmoji, #paymentAddress") |             "SET #commitment = if_not_exists(#commitment, :commitment), #name = :name REMOVE #avatar, #about, #aboutEmoji, #paymentAddress, #phoneNumberSharing"), | ||||||
|  | 
 | ||||||
|  |         Arguments.of( | ||||||
|  |             new VersionedProfile(version, null, null, null, null, null, null, commitment), | ||||||
|  |             "SET #commitment = if_not_exists(#commitment, :commitment) REMOVE #name, #avatar, #about, #aboutEmoji, #paymentAddress, #phoneNumberSharing") | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | @ -308,11 +317,23 @@ public class ProfilesTest { | ||||||
|     final byte[] emoji = TestRandomUtil.nextBytes(60); |     final byte[] emoji = TestRandomUtil.nextBytes(60); | ||||||
|     final byte[] about = TestRandomUtil.nextBytes(156); |     final byte[] about = TestRandomUtil.nextBytes(156); | ||||||
|     final byte[] paymentAddress = TestRandomUtil.nextBytes(582); |     final byte[] paymentAddress = TestRandomUtil.nextBytes(582); | ||||||
|  |     final byte[] phoneNumberSharing = TestRandomUtil.nextBytes(29); | ||||||
|     final byte[] commitment = new ProfileKey(TestRandomUtil.nextBytes(32)).getCommitment(new ServiceId.Aci(ACI)).serialize(); |     final byte[] commitment = new ProfileKey(TestRandomUtil.nextBytes(32)).getCommitment(new ServiceId.Aci(ACI)).serialize(); | ||||||
| 
 | 
 | ||||||
|     return Stream.of( |     return Stream.of( | ||||||
|         Arguments.of( |         Arguments.of( | ||||||
|             new VersionedProfile(version, name, avatar, emoji, about, paymentAddress, commitment), |             new VersionedProfile(version, name, avatar, emoji, about, paymentAddress, phoneNumberSharing, commitment), | ||||||
|  |             Map.of( | ||||||
|  |                 ":commitment", AttributeValues.fromByteArray(commitment), | ||||||
|  |                 ":name", AttributeValues.fromByteArray(name), | ||||||
|  |                 ":avatar", AttributeValues.fromString(avatar), | ||||||
|  |                 ":aboutEmoji", AttributeValues.fromByteArray(emoji), | ||||||
|  |                 ":about", AttributeValues.fromByteArray(about), | ||||||
|  |                 ":paymentAddress", AttributeValues.fromByteArray(paymentAddress), | ||||||
|  |                 ":phoneNumberSharing", AttributeValues.fromByteArray(phoneNumberSharing))), | ||||||
|  | 
 | ||||||
|  |         Arguments.of( | ||||||
|  |             new VersionedProfile(version, name, avatar, emoji, about, paymentAddress, null, commitment), | ||||||
|             Map.of( |             Map.of( | ||||||
|                 ":commitment", AttributeValues.fromByteArray(commitment), |                 ":commitment", AttributeValues.fromByteArray(commitment), | ||||||
|                 ":name", AttributeValues.fromByteArray(name), |                 ":name", AttributeValues.fromByteArray(name), | ||||||
|  | @ -322,7 +343,7 @@ public class ProfilesTest { | ||||||
|                 ":paymentAddress", AttributeValues.fromByteArray(paymentAddress))), |                 ":paymentAddress", AttributeValues.fromByteArray(paymentAddress))), | ||||||
| 
 | 
 | ||||||
|         Arguments.of( |         Arguments.of( | ||||||
|             new VersionedProfile(version, name, avatar, emoji, about, null, commitment), |             new VersionedProfile(version, name, avatar, emoji, about, null, null, commitment), | ||||||
|             Map.of( |             Map.of( | ||||||
|                 ":commitment", AttributeValues.fromByteArray(commitment), |                 ":commitment", AttributeValues.fromByteArray(commitment), | ||||||
|                 ":name", AttributeValues.fromByteArray(name), |                 ":name", AttributeValues.fromByteArray(name), | ||||||
|  | @ -331,7 +352,7 @@ public class ProfilesTest { | ||||||
|                 ":about", AttributeValues.fromByteArray(about))), |                 ":about", AttributeValues.fromByteArray(about))), | ||||||
| 
 | 
 | ||||||
|         Arguments.of( |         Arguments.of( | ||||||
|             new VersionedProfile(version, name, avatar, emoji, null, null, commitment), |             new VersionedProfile(version, name, avatar, emoji, null, null, null, commitment), | ||||||
|             Map.of( |             Map.of( | ||||||
|                 ":commitment", AttributeValues.fromByteArray(commitment), |                 ":commitment", AttributeValues.fromByteArray(commitment), | ||||||
|                 ":name",AttributeValues.fromByteArray(name), |                 ":name",AttributeValues.fromByteArray(name), | ||||||
|  | @ -339,20 +360,20 @@ public class ProfilesTest { | ||||||
|                 ":aboutEmoji", AttributeValues.fromByteArray(emoji))), |                 ":aboutEmoji", AttributeValues.fromByteArray(emoji))), | ||||||
| 
 | 
 | ||||||
|         Arguments.of( |         Arguments.of( | ||||||
|             new VersionedProfile(version, name, avatar, null, null, null, commitment), |             new VersionedProfile(version, name, avatar, null, null, null, null, commitment), | ||||||
|             Map.of( |             Map.of( | ||||||
|                 ":commitment", AttributeValues.fromByteArray(commitment), |                 ":commitment", AttributeValues.fromByteArray(commitment), | ||||||
|                 ":name", AttributeValues.fromByteArray(name), |                 ":name", AttributeValues.fromByteArray(name), | ||||||
|                 ":avatar", AttributeValues.fromString(avatar))), |                 ":avatar", AttributeValues.fromString(avatar))), | ||||||
| 
 | 
 | ||||||
|         Arguments.of( |         Arguments.of( | ||||||
|             new VersionedProfile(version, name, null, null, null, null, commitment), |             new VersionedProfile(version, name, null, null, null, null, null, commitment), | ||||||
|             Map.of( |             Map.of( | ||||||
|                 ":commitment", AttributeValues.fromByteArray(commitment), |                 ":commitment", AttributeValues.fromByteArray(commitment), | ||||||
|                 ":name", AttributeValues.fromByteArray(name))), |                 ":name", AttributeValues.fromByteArray(name))), | ||||||
| 
 | 
 | ||||||
|         Arguments.of( |         Arguments.of( | ||||||
|             new VersionedProfile(version, null, null, null, null, null, commitment), |             new VersionedProfile(version, null, null, null, null, null, null, commitment), | ||||||
|             Map.of(":commitment", AttributeValues.fromByteArray(commitment))) |             Map.of(":commitment", AttributeValues.fromByteArray(commitment))) | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Jonathan Klabunde Tomer
						Jonathan Klabunde Tomer