From a87b84fbe2acedcfb32f3c07b66d1abd41370dd3 Mon Sep 17 00:00:00 2001 From: Jon Chambers Date: Thu, 16 Dec 2021 10:23:40 -0500 Subject: [PATCH] Return an empty response if somebody requests a profile key credential with a non-existent version --- .../controllers/ProfileController.java | 14 +++--- .../PniCredentialProfileResponse.java | 9 ++-- .../ProfileKeyCredentialProfileResponse.java | 8 ++-- .../controllers/ProfileControllerTest.java | 47 +++++++++++++++++++ 4 files changed, 62 insertions(+), 16 deletions(-) diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java index 9ab389666..d2902ad07 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java @@ -296,12 +296,13 @@ public class ProfileController { final boolean isSelf, final ContainerRequestContext containerRequestContext) { - final VersionedProfile profile = - profilesManager.get(account.getUuid(), version).orElseThrow(NotFoundException::new); + final ProfileKeyCredentialResponse profileKeyCredentialResponse = profilesManager.get(account.getUuid(), version) + .map(profile -> getProfileCredential(encodedCredentialRequest, profile, account.getUuid())) + .orElse(null); return new ProfileKeyCredentialProfileResponse( buildVersionedProfileResponse(account, version, isSelf, containerRequestContext), - getProfileCredential(encodedCredentialRequest, profile, account.getUuid())); + profileKeyCredentialResponse); } private PniCredentialProfileResponse buildPniCredentialProfileResponse(final Account account, @@ -309,12 +310,13 @@ public class ProfileController { final String encodedCredentialRequest, final ContainerRequestContext containerRequestContext) { - final VersionedProfile profile = - profilesManager.get(account.getUuid(), version).orElseThrow(NotFoundException::new); + final PniCredentialResponse pniCredentialResponse = profilesManager.get(account.getUuid(), version) + .map(profile -> getPniCredential(encodedCredentialRequest, profile, account.getUuid(), account.getPhoneNumberIdentifier())) + .orElse(null); return new PniCredentialProfileResponse( buildVersionedProfileResponse(account, version, true, containerRequestContext), - getPniCredential(encodedCredentialRequest, profile, account.getUuid(), account.getPhoneNumberIdentifier())); + pniCredentialResponse); } private VersionedProfileResponse buildVersionedProfileResponse(final Account account, diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/PniCredentialProfileResponse.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/PniCredentialProfileResponse.java index 1bc903e7b..ae4df34ae 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/PniCredentialProfileResponse.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/PniCredentialProfileResponse.java @@ -5,32 +5,31 @@ 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; +import org.signal.zkgroup.profiles.PniCredentialResponse; public class PniCredentialProfileResponse extends CredentialProfileResponse { @JsonProperty @JsonSerialize(using = PniCredentialResponseAdapter.Serializing.class) @JsonDeserialize(using = PniCredentialResponseAdapter.Deserializing.class) + @Nullable private PniCredentialResponse pniCredential; public PniCredentialProfileResponse() { } public PniCredentialProfileResponse(final VersionedProfileResponse versionedProfileResponse, - final PniCredentialResponse pniCredential) { + @Nullable final PniCredentialResponse pniCredential) { super(versionedProfileResponse); this.pniCredential = pniCredential; } + @Nullable public PniCredentialResponse getPniCredential() { return pniCredential; } diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/entities/ProfileKeyCredentialProfileResponse.java b/service/src/main/java/org/whispersystems/textsecuregcm/entities/ProfileKeyCredentialProfileResponse.java index 4477e31e5..a642e63ea 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/entities/ProfileKeyCredentialProfileResponse.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/entities/ProfileKeyCredentialProfileResponse.java @@ -5,33 +5,31 @@ 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) + @Nullable private ProfileKeyCredentialResponse credential; public ProfileKeyCredentialProfileResponse() { } public ProfileKeyCredentialProfileResponse(final VersionedProfileResponse versionedProfileResponse, - final ProfileKeyCredentialResponse credential) { + @Nullable final ProfileKeyCredentialResponse credential) { super(versionedProfileResponse); this.credential = credential; } + @Nullable public ProfileKeyCredentialResponse getCredential() { return credential; } diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ProfileControllerTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ProfileControllerTest.java index ac51fac06..a99a15bc2 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ProfileControllerTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/tests/controllers/ProfileControllerTest.java @@ -768,6 +768,29 @@ class ProfileControllerTest { ); } + @Test + void testGetProfileWithProfileKeyCredentialVersionNotFound() throws VerificationFailedException { + final Account account = mock(Account.class); + when(account.getUuid()).thenReturn(AuthHelper.VALID_UUID); + when(account.getCurrentProfileVersion()).thenReturn(Optional.of("version")); + when(account.isEnabled()).thenReturn(true); + + when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(account)); + when(profilesManager.get(any(), any())).thenReturn(Optional.empty()); + + final ProfileKeyCredentialProfileResponse profile = resources.getJerseyTest() + .target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, "version-that-does-not-exist", "credential-request")) + .request() + .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) + .get(ProfileKeyCredentialProfileResponse.class); + + assertThat(profile.getVersionedProfileResponse().getBaseProfileResponse().getUuid()).isEqualTo(AuthHelper.VALID_UUID); + assertThat(profile.getCredential()).isNull(); + + verify(zkProfileOperations, never()).issueProfileKeyCredential(any(), any(), any()); + verify(zkProfileOperations, never()).issuePniCredential(any(), any(), any(), any()); + } + @Test void testGetProfileWithPniCredential() throws InvalidInputException, VerificationFailedException { final String version = "version"; @@ -866,6 +889,30 @@ class ProfileControllerTest { verify(zkProfileOperations, never()).issuePniCredential(any(), any(), any(), any()); } + @Test + void testGetProfileWithPniCredentialVersionNotFound() throws VerificationFailedException { + final Account account = mock(Account.class); + when(account.getUuid()).thenReturn(AuthHelper.VALID_UUID); + when(account.getCurrentProfileVersion()).thenReturn(Optional.of("version")); + when(account.isEnabled()).thenReturn(true); + + when(accountsManager.getByAccountIdentifier(AuthHelper.VALID_UUID)).thenReturn(Optional.of(account)); + when(profilesManager.get(any(), any())).thenReturn(Optional.empty()); + + final PniCredentialProfileResponse profile = resources.getJerseyTest() + .target(String.format("/v1/profile/%s/%s/%s", AuthHelper.VALID_UUID, "version-that-does-not-exist", "credential-request")) + .queryParam("credentialType", "pni") + .request() + .header("Authorization", AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD)) + .get(PniCredentialProfileResponse.class); + + assertThat(profile.getVersionedProfileResponse().getBaseProfileResponse().getUuid()).isEqualTo(AuthHelper.VALID_UUID); + assertThat(profile.getPniCredential()).isNull(); + + verify(zkProfileOperations, never()).issueProfileKeyCredential(any(), any(), any()); + verify(zkProfileOperations, never()).issuePniCredential(any(), any(), any(), any()); + } + @Test void testSetProfileBadgesMissingFromRequest() throws InvalidInputException { ProfileKeyCommitment commitment = new ProfileKey(new byte[32]).getCommitment(AuthHelper.VALID_UUID);