Refactor: separate the various types of profile responses

This commit is contained in:
Jon Chambers
2021-12-07 11:35:32 -05:00
committed by Jon Chambers
parent 4ea7278c6f
commit 66845d7080
9 changed files with 463 additions and 351 deletions

View File

@@ -8,6 +8,8 @@ package org.whispersystems.textsecuregcm.auth;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.Device;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import java.security.MessageDigest;
@@ -36,9 +38,9 @@ public class OptionalAccess {
}
if (requestAccount.isPresent()) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
throw new NotFoundException();
} else {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
throw new NotAuthorizedException(Response.Status.UNAUTHORIZED);
}
}
} catch (NumberFormatException e) {
@@ -56,7 +58,7 @@ public class OptionalAccess {
//noinspection ConstantConditions
if (requestAccount.isPresent() && (targetAccount.isEmpty() || (targetAccount.isPresent() && !targetAccount.get().isEnabled()))) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
throw new NotFoundException();
}
if (accessKey.isPresent() && targetAccount.isPresent() && targetAccount.get().isEnabled() && targetAccount.get().isUnrestrictedUnidentifiedAccess()) {
@@ -72,7 +74,7 @@ public class OptionalAccess {
return;
}
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
throw new NotAuthorizedException(Response.Status.UNAUTHORIZED);
}
}

View File

@@ -29,6 +29,8 @@ import javax.ws.rs.DefaultValue;
import javax.ws.rs.ForbiddenException;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.NotAuthorizedException;
import javax.ws.rs.NotFoundException;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
@@ -63,9 +65,13 @@ import org.whispersystems.textsecuregcm.configuration.BadgeConfiguration;
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
import org.whispersystems.textsecuregcm.entities.CreateProfileRequest;
import org.whispersystems.textsecuregcm.entities.Profile;
import org.whispersystems.textsecuregcm.entities.CredentialProfileResponse;
import org.whispersystems.textsecuregcm.entities.PniCredentialProfileResponse;
import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes;
import org.whispersystems.textsecuregcm.entities.ProfileKeyCredentialProfileResponse;
import org.whispersystems.textsecuregcm.entities.BaseProfileResponse;
import org.whispersystems.textsecuregcm.entities.UserCapabilities;
import org.whispersystems.textsecuregcm.entities.VersionedProfileResponse;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.s3.PolicySigner;
import org.whispersystems.textsecuregcm.s3.PostPolicyGenerator;
@@ -196,23 +202,28 @@ public class ProfileController {
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/{uuid}/{version}")
public Profile getProfile(
public VersionedProfileResponse getProfile(
@Auth Optional<AuthenticatedAccount> auth,
@HeaderParam(OptionalAccess.UNIDENTIFIED) Optional<Anonymous> accessKey,
@Context ContainerRequestContext containerRequestContext,
@PathParam("uuid") UUID uuid,
@PathParam("version") String version)
throws RateLimitExceededException {
return getVersionedProfile(auth.map(AuthenticatedAccount::getAccount), accessKey,
getAcceptableLanguagesForRequest(containerRequestContext), uuid,
version, Optional.empty(), Optional.empty());
final Optional<Account> maybeRequester = auth.map(AuthenticatedAccount::getAccount);
final Account targetAccount = verifyPermissionToReceiveProfile(maybeRequester, accessKey, uuid);
return buildVersionedProfileResponse(targetAccount,
version,
isSelfProfileRequest(maybeRequester, uuid),
containerRequestContext);
}
@Timed
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/{uuid}/{version}/{credentialRequest}")
public Profile getProfile(
public CredentialProfileResponse getProfile(
@Auth Optional<AuthenticatedAccount> auth,
@HeaderParam(OptionalAccess.UNIDENTIFIED) Optional<Anonymous> accessKey,
@Context ContainerRequestContext containerRequestContext,
@@ -221,134 +232,140 @@ public class ProfileController {
@PathParam("credentialRequest") String credentialRequest,
@QueryParam("credentialType") @DefaultValue(PROFILE_KEY_CREDENTIAL_TYPE) String credentialType)
throws RateLimitExceededException {
return getVersionedProfile(auth.map(AuthenticatedAccount::getAccount), accessKey,
getAcceptableLanguagesForRequest(containerRequestContext), uuid,
version, Optional.of(credentialRequest), Optional.of(credentialType));
}
private Profile getVersionedProfile(
Optional<Account> requestAccount,
Optional<Anonymous> accessKey,
List<Locale> acceptableLanguages,
UUID uuid,
String version,
Optional<String> credentialRequest,
Optional<String> credentialType)
throws RateLimitExceededException {
if (requestAccount.isEmpty() && accessKey.isEmpty()) {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
final Optional<Account> maybeRequester = auth.map(AuthenticatedAccount::getAccount);
final Account targetAccount = verifyPermissionToReceiveProfile(maybeRequester, accessKey, uuid);
final boolean isSelf = isSelfProfileRequest(maybeRequester, uuid);
boolean isSelf = false;
if (requestAccount.isPresent()) {
UUID authedUuid = requestAccount.get().getUuid();
rateLimiters.getProfileLimiter().validate(authedUuid);
isSelf = uuid.equals(authedUuid);
}
switch (credentialType) {
case PROFILE_KEY_CREDENTIAL_TYPE -> {
return buildProfileKeyCredentialProfileResponse(targetAccount,
version,
credentialRequest,
isSelf,
containerRequestContext);
}
Optional<Account> accountProfile = accountsManager.getByAccountIdentifier(uuid);
OptionalAccess.verify(requestAccount, accessKey, accountProfile);
assert accountProfile.isPresent();
Optional<String> username = accountProfile.flatMap(Account::getUsername);
Optional<VersionedProfile> profile = profilesManager.get(uuid, version);
String name = profile.map(VersionedProfile::getName).orElse(null);
String about = profile.map(VersionedProfile::getAbout).orElse(null);
String aboutEmoji = profile.map(VersionedProfile::getAboutEmoji).orElse(null);
String avatar = profile.map(VersionedProfile::getAvatar).orElse(null);
Optional<String> currentProfileVersion = accountProfile.get().getCurrentProfileVersion();
// 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 String paymentAddress = profile
.filter(p -> currentProfileVersion.map(v -> v.equals(version)).orElse(true))
.map(VersionedProfile::getPaymentAddress)
.orElse(null);
final ProfileKeyCredentialResponse profileKeyCredentialResponse;
final PniCredentialResponse pniCredentialResponse;
if (credentialRequest.isPresent() && credentialType.isPresent() && profile.isPresent()) {
if (PNI_CREDENTIAL_TYPE.equals(credentialType.get())) {
case PNI_CREDENTIAL_TYPE -> {
if (!isSelf) {
throw new ForbiddenException();
}
profileKeyCredentialResponse = null;
pniCredentialResponse = getPniCredential(credentialRequest.get(),
profile.get(),
requestAccount.get().getUuid(),
requestAccount.get().getPhoneNumberIdentifier());
} else if (PROFILE_KEY_CREDENTIAL_TYPE.equals(credentialType.get())) {
profileKeyCredentialResponse = getProfileCredential(credentialRequest.get(),
profile.get(),
uuid);
pniCredentialResponse = null;
} else {
throw new BadRequestException();
return buildPniCredentialProfileResponse(targetAccount,
version,
credentialRequest,
containerRequestContext);
}
} else {
profileKeyCredentialResponse = null;
pniCredentialResponse = null;
}
return new Profile(
name,
about,
aboutEmoji,
avatar,
paymentAddress,
accountProfile.get().getIdentityKey(),
UnidentifiedAccessChecksum.generateFor(accountProfile.get().getUnidentifiedAccessKey()),
accountProfile.get().isUnrestrictedUnidentifiedAccess(),
UserCapabilities.createForAccount(accountProfile.get()),
username.orElse(null),
null,
profileBadgeConverter.convert(acceptableLanguages, accountProfile.get().getBadges(), isSelf),
profileKeyCredentialResponse,
pniCredentialResponse);
default -> throw new BadRequestException();
}
}
// Although clients should generally be using versioned profiles wherever possible, there are still a few lingering
// use cases for getting profiles without a version (e.g. getting a contact's unidentified access key checksum).
@Timed
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/{identifier}")
public BaseProfileResponse getUnversionedProfile(
@Auth Optional<AuthenticatedAccount> auth,
@HeaderParam(OptionalAccess.UNIDENTIFIED) Optional<Anonymous> accessKey,
@Context ContainerRequestContext containerRequestContext,
@HeaderParam("User-Agent") String userAgent,
@PathParam("identifier") UUID identifier,
@QueryParam("ca") boolean useCaCertificate)
throws RateLimitExceededException {
final Optional<Account> maybeRequester = auth.map(AuthenticatedAccount::getAccount);
final Account targetAccount = verifyPermissionToReceiveProfile(maybeRequester, accessKey, identifier);
return buildBaseProfileResponse(targetAccount,
isSelfProfileRequest(maybeRequester, identifier),
containerRequestContext);
}
private ProfileKeyCredentialProfileResponse buildProfileKeyCredentialProfileResponse(final Account account,
final String version,
final String encodedCredentialRequest,
final boolean isSelf,
final ContainerRequestContext containerRequestContext) {
final VersionedProfile profile =
profilesManager.get(account.getUuid(), version).orElseThrow(NotFoundException::new);
return new ProfileKeyCredentialProfileResponse(
buildVersionedProfileResponse(account, version, isSelf, containerRequestContext),
getProfileCredential(encodedCredentialRequest, profile, account.getUuid()));
}
private PniCredentialProfileResponse buildPniCredentialProfileResponse(final Account account,
final String version,
final String encodedCredentialRequest,
final ContainerRequestContext containerRequestContext) {
final VersionedProfile profile =
profilesManager.get(account.getUuid(), version).orElseThrow(NotFoundException::new);
return new PniCredentialProfileResponse(
buildVersionedProfileResponse(account, version, true, containerRequestContext),
getPniCredential(encodedCredentialRequest, profile, account.getUuid(), account.getPhoneNumberIdentifier()));
}
private VersionedProfileResponse buildVersionedProfileResponse(final Account account,
final String version,
final boolean isSelf,
final ContainerRequestContext containerRequestContext) {
final Optional<VersionedProfile> maybeProfile = profilesManager.get(account.getUuid(), version);
final String name = maybeProfile.map(VersionedProfile::getName).orElse(null);
final String about = maybeProfile.map(VersionedProfile::getAbout).orElse(null);
final String aboutEmoji = maybeProfile.map(VersionedProfile::getAboutEmoji).orElse(null);
final String avatar = maybeProfile.map(VersionedProfile::getAvatar).orElse(null);
// 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 String paymentAddress = maybeProfile
.filter(p -> account.getCurrentProfileVersion().map(v -> v.equals(version)).orElse(true))
.map(VersionedProfile::getPaymentAddress)
.orElse(null);
return new VersionedProfileResponse(buildBaseProfileResponse(account, isSelf, containerRequestContext),
name, about, aboutEmoji, avatar, paymentAddress);
}
private BaseProfileResponse buildBaseProfileResponse(final Account account,
final boolean isSelf,
final ContainerRequestContext containerRequestContext) {
return new BaseProfileResponse(account.getIdentityKey(),
UnidentifiedAccessChecksum.generateFor(account.getUnidentifiedAccessKey()),
account.isUnrestrictedUnidentifiedAccess(),
UserCapabilities.createForAccount(account),
profileBadgeConverter.convert(
getAcceptableLanguagesForRequest(containerRequestContext),
account.getBadges(),
isSelf),
isSelf ? account.getUsername().orElse(null) : null,
account.getUuid());
}
@Timed
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/username/{username}")
public Profile getProfileByUsername(
public BaseProfileResponse getProfileByUsername(
@Auth AuthenticatedAccount auth,
@Context ContainerRequestContext containerRequestContext,
@PathParam("username") String username)
throws RateLimitExceededException {
rateLimiters.getUsernameLookupLimiter().validate(auth.getAccount().getUuid());
username = username.toLowerCase();
final Account targetAccount = accountsManager.getByUsername(username).orElseThrow(NotFoundException::new);
final boolean isSelf = auth.getAccount().getUuid().equals(targetAccount.getUuid());
final Account accountProfile = accountsManager.getByUsername(username)
.orElseThrow(() -> new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build()));
final boolean isSelf = auth.getAccount().getUuid().equals(accountProfile.getUuid());
return new Profile(
null,
null,
null,
null,
null,
accountProfile.getIdentityKey(),
UnidentifiedAccessChecksum.generateFor(accountProfile.getUnidentifiedAccessKey()),
accountProfile.isUnrestrictedUnidentifiedAccess(),
UserCapabilities.createForAccount(accountProfile),
username,
accountProfile.getUuid(),
profileBadgeConverter.convert(
getAcceptableLanguagesForRequest(containerRequestContext),
accountProfile.getBadges(),
isSelf),
null,
null);
return buildBaseProfileResponse(targetAccount, isSelf, containerRequestContext);
}
private ProfileKeyCredentialResponse getProfileCredential(final String encodedProfileCredentialRequest,
@@ -379,57 +396,6 @@ public class ProfileController {
}
}
// Although clients should generally be using versioned profiles wherever possible, there are still a few lingering
// use cases for getting profiles without a version (e.g. getting a contact's unidentified access key checksum).
@Timed
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/{identifier}")
public Profile getUnversionedProfile(
@Auth Optional<AuthenticatedAccount> auth,
@HeaderParam(OptionalAccess.UNIDENTIFIED) Optional<Anonymous> accessKey,
@Context ContainerRequestContext containerRequestContext,
@HeaderParam("User-Agent") String userAgent,
@PathParam("identifier") UUID identifier,
@QueryParam("ca") boolean useCaCertificate)
throws RateLimitExceededException {
if (auth.isEmpty() && accessKey.isEmpty()) {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
boolean isSelf = false;
if (auth.isPresent()) {
UUID authedUuid = auth.get().getAccount().getUuid();
rateLimiters.getProfileLimiter().validate(authedUuid);
isSelf = authedUuid.equals(identifier);
}
Optional<Account> accountProfile = accountsManager.getByAccountIdentifier(identifier);
OptionalAccess.verify(auth.map(AuthenticatedAccount::getAccount), accessKey, accountProfile);
Optional<String> username = accountProfile.flatMap(Account::getUsername);
return new Profile(
null,
null,
null,
null,
null,
accountProfile.get().getIdentityKey(),
UnidentifiedAccessChecksum.generateFor(accountProfile.get().getUnidentifiedAccessKey()),
accountProfile.get().isUnrestrictedUnidentifiedAccess(),
UserCapabilities.createForAccount(accountProfile.get()),
username.orElse(null),
null,
profileBadgeConverter.convert(
getAcceptableLanguagesForRequest(containerRequestContext),
accountProfile.get().getBadges(),
isSelf),
null,
null);
}
private ProfileAvatarUploadAttributes generateAvatarUploadForm(String objectName) {
ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
Pair<String, String> policy = policyGenerator.createFor(now, objectName, 10 * 1024 * 1024);
@@ -497,4 +463,42 @@ public class ProfileController {
return new ArrayList<>(result.values());
}
/**
* Verifies that the requester has permission to view the profile of the account identified by the given ACI.
*
* @param maybeRequester the authenticated account requesting the profile, if any
* @param maybeAccessKey an anonymous access key for the target account
* @param targetUuid the ACI of the target account
*
* @return the target account
*
* @throws RateLimitExceededException if the requester must wait before requesting the target account's profile
* @throws NotFoundException if no account was found for the target ACI
* @throws NotAuthorizedException if the requester is not authorized to receive the target account's profile or if the
* requester was not authenticated and did not present an anonymous access key
*/
private Account verifyPermissionToReceiveProfile(final Optional<Account> maybeRequester,
final Optional<Anonymous> maybeAccessKey,
final UUID targetUuid) throws RateLimitExceededException {
if (maybeRequester.isEmpty() && maybeAccessKey.isEmpty()) {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
if (maybeRequester.isPresent()) {
rateLimiters.getProfileLimiter().validate(maybeRequester.get().getUuid());
}
final Optional<Account> maybeTargetAccount = accountsManager.getByAccountIdentifier(targetUuid);
OptionalAccess.verify(maybeRequester, maybeAccessKey, maybeTargetAccount);
assert maybeTargetAccount.isPresent();
return maybeTargetAccount.get();
}
private boolean isSelfProfileRequest(final Optional<Account> maybeRequester, final UUID targetUuid) {
return maybeRequester.map(requester -> requester.getUuid().equals(targetUuid)).orElse(false);
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import javax.annotation.Nullable;
import java.util.List;
import java.util.UUID;
public class BaseProfileResponse {
@JsonProperty
private String identityKey;
@JsonProperty
private String unidentifiedAccess;
@JsonProperty
private boolean unrestrictedUnidentifiedAccess;
@JsonProperty
private UserCapabilities capabilities;
@JsonProperty
private List<Badge> badges;
@JsonProperty
private String username;
@JsonProperty
private UUID uuid;
public BaseProfileResponse() {
}
public BaseProfileResponse(final String identityKey,
final String unidentifiedAccess,
final boolean unrestrictedUnidentifiedAccess,
final UserCapabilities capabilities,
final List<Badge> badges,
@Nullable final String username,
final UUID uuid) {
this.identityKey = identityKey;
this.unidentifiedAccess = unidentifiedAccess;
this.unrestrictedUnidentifiedAccess = unrestrictedUnidentifiedAccess;
this.capabilities = capabilities;
this.badges = badges;
this.username = username;
this.uuid = uuid;
}
public String getIdentityKey() {
return identityKey;
}
public String getUnidentifiedAccess() {
return unidentifiedAccess;
}
public boolean isUnrestrictedUnidentifiedAccess() {
return unrestrictedUnidentifiedAccess;
}
public UserCapabilities getCapabilities() {
return capabilities;
}
public List<Badge> getBadges() {
return badges;
}
public String getUsername() {
return username;
}
public UUID getUuid() {
return uuid;
}
}

View File

@@ -0,0 +1,25 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
public abstract class CredentialProfileResponse {
@JsonUnwrapped
private VersionedProfileResponse versionedProfileResponse;
protected CredentialProfileResponse() {
}
protected CredentialProfileResponse(final VersionedProfileResponse versionedProfileResponse) {
this.versionedProfileResponse = versionedProfileResponse;
}
public VersionedProfileResponse getVersionedProfileResponse() {
return versionedProfileResponse;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.entities;
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.JsonSerialize;
import org.signal.zkgroup.profiles.PniCredentialResponse;
import javax.annotation.Nullable;
import java.util.List;
import java.util.UUID;
public class PniCredentialProfileResponse extends CredentialProfileResponse {
@JsonProperty
@JsonSerialize(using = PniCredentialResponseAdapter.Serializing.class)
@JsonDeserialize(using = PniCredentialResponseAdapter.Deserializing.class)
private PniCredentialResponse pniCredential;
public PniCredentialProfileResponse() {
}
public PniCredentialProfileResponse(final VersionedProfileResponse versionedProfileResponse,
final PniCredentialResponse pniCredential) {
super(versionedProfileResponse);
this.pniCredential = pniCredential;
}
public PniCredentialResponse getPniCredential() {
return pniCredential;
}
}

View File

@@ -1,151 +0,0 @@
/*
* Copyright 2013-2020 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.annotations.VisibleForTesting;
import java.util.List;
import java.util.UUID;
import org.signal.zkgroup.profiles.PniCredentialResponse;
import org.signal.zkgroup.profiles.ProfileKeyCredentialResponse;
public class Profile {
@JsonProperty
private String identityKey;
@JsonProperty
private String name;
@JsonProperty
private String about;
@JsonProperty
private String aboutEmoji;
@JsonProperty
private String avatar;
@JsonProperty
private String paymentAddress;
@JsonProperty
private String unidentifiedAccess;
@JsonProperty
private boolean unrestrictedUnidentifiedAccess;
@JsonProperty
private UserCapabilities capabilities;
@JsonProperty
private String username;
@JsonProperty
private UUID uuid;
@JsonProperty
private List<Badge> badges;
@JsonProperty
@JsonSerialize(using = ProfileKeyCredentialResponseAdapter.Serializing.class)
@JsonDeserialize(using = ProfileKeyCredentialResponseAdapter.Deserializing.class)
private ProfileKeyCredentialResponse credential;
@JsonProperty
@JsonSerialize(using = PniCredentialResponseAdapter.Serializing.class)
@JsonDeserialize(using = PniCredentialResponseAdapter.Deserializing.class)
private PniCredentialResponse pniCredential;
public Profile() {}
public Profile(
String name, String about, String aboutEmoji, String avatar, String paymentAddress, String identityKey,
String unidentifiedAccess, boolean unrestrictedUnidentifiedAccess, UserCapabilities capabilities, String username,
UUID uuid, List<Badge> badges, ProfileKeyCredentialResponse credential, PniCredentialResponse pniCredential)
{
this.name = name;
this.about = about;
this.aboutEmoji = aboutEmoji;
this.avatar = avatar;
this.paymentAddress = paymentAddress;
this.identityKey = identityKey;
this.unidentifiedAccess = unidentifiedAccess;
this.unrestrictedUnidentifiedAccess = unrestrictedUnidentifiedAccess;
this.capabilities = capabilities;
this.username = username;
this.uuid = uuid;
this.badges = badges;
this.credential = credential;
this.pniCredential = pniCredential;
}
@VisibleForTesting
public String getIdentityKey() {
return identityKey;
}
@VisibleForTesting
public String getName() {
return name;
}
public String getAbout() {
return about;
}
public String getAboutEmoji() {
return aboutEmoji;
}
@VisibleForTesting
public String getAvatar() {
return avatar;
}
public String getPaymentAddress() {
return paymentAddress;
}
@VisibleForTesting
public String getUnidentifiedAccess() {
return unidentifiedAccess;
}
@VisibleForTesting
public boolean isUnrestrictedUnidentifiedAccess() {
return unrestrictedUnidentifiedAccess;
}
@VisibleForTesting
public UserCapabilities getCapabilities() {
return capabilities;
}
@VisibleForTesting
public String getUsername() {
return username;
}
@VisibleForTesting
public UUID getUuid() {
return uuid;
}
public List<Badge> getBadges() {
return badges;
}
public ProfileKeyCredentialResponse getCredential() {
return credential;
}
public PniCredentialResponse getPniCredential() {
return pniCredential;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.entities;
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.JsonSerialize;
import org.signal.zkgroup.profiles.PniCredentialResponse;
import org.signal.zkgroup.profiles.ProfileKeyCredentialResponse;
import javax.annotation.Nullable;
import java.util.List;
import java.util.UUID;
public class ProfileKeyCredentialProfileResponse extends CredentialProfileResponse {
@JsonProperty
@JsonSerialize(using = ProfileKeyCredentialResponseAdapter.Serializing.class)
@JsonDeserialize(using = ProfileKeyCredentialResponseAdapter.Deserializing.class)
private ProfileKeyCredentialResponse credential;
public ProfileKeyCredentialProfileResponse() {
}
public ProfileKeyCredentialProfileResponse(final VersionedProfileResponse versionedProfileResponse,
final ProfileKeyCredentialResponse credential) {
super(versionedProfileResponse);
this.credential = credential;
}
public ProfileKeyCredentialResponse getCredential() {
return credential;
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.entities;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import javax.annotation.Nullable;
import java.util.List;
import java.util.UUID;
public class VersionedProfileResponse {
@JsonUnwrapped
private BaseProfileResponse baseProfileResponse;
@JsonProperty
private String name;
@JsonProperty
private String about;
@JsonProperty
private String aboutEmoji;
@JsonProperty
private String avatar;
@JsonProperty
private String paymentAddress;
public VersionedProfileResponse() {
}
public VersionedProfileResponse(final BaseProfileResponse baseProfileResponse,
final String name,
final String about,
final String aboutEmoji,
final String avatar,
final String paymentAddress) {
this.baseProfileResponse = baseProfileResponse;
this.name = name;
this.about = about;
this.aboutEmoji = aboutEmoji;
this.avatar = avatar;
this.paymentAddress = paymentAddress;
}
public BaseProfileResponse getBaseProfileResponse() {
return baseProfileResponse;
}
public String getName() {
return name;
}
public String getAbout() {
return about;
}
public String getAboutEmoji() {
return aboutEmoji;
}
public String getAvatar() {
return avatar;
}
public String getPaymentAddress() {
return paymentAddress;
}
}