Wire up stored account badges to the profile endpoints

This commit is contained in:
Ehren Kret 2021-09-03 15:49:27 -05:00
parent bc887ec6fa
commit fc1465c05d
5 changed files with 64 additions and 26 deletions

View File

@ -43,6 +43,7 @@ import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
@ -68,6 +69,7 @@ import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccountAuthenticat
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
import org.whispersystems.textsecuregcm.badges.ProfileBadgeConverter;
import org.whispersystems.textsecuregcm.configuration.DirectoryServerConfiguration;
import org.whispersystems.textsecuregcm.controllers.AccountController;
import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV1;
@ -288,6 +290,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
environment.getObjectMapper().setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
environment.getObjectMapper().setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
ProfileBadgeConverter profileBadgeConverter = (request, accountBadges) -> Set.of(); // TODO: Provide an actual implementation.
JdbiFactory jdbiFactory = new JdbiFactory(DefaultNameStrategy.CHECK_EMPTY);
Jdbi accountJdbi = jdbiFactory.build(environment, config.getAccountsDatabaseConfiguration(), "accountdb");
Jdbi abuseJdbi = jdbiFactory.build(environment, config.getAbuseDatabaseConfiguration(), "abusedb" );
@ -586,7 +590,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
new DonationController(donationExecutor, config.getDonationConfiguration()),
new MessageController(rateLimiters, messageSender, receiptSender, accountsManager, messagesManager, unsealedSenderRateLimiter, apnFallbackManager, dynamicConfigurationManager, rateLimitChallengeManager, reportMessageManager, metricsCluster, declinedMessageReceiptExecutor, multiRecipientMessageExecutor),
new PaymentsController(currencyManager, paymentsCredentialsGenerator),
new ProfileController(rateLimiters, accountsManager, profilesManager, usernamesManager, dynamicConfigurationManager, cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner, config.getCdnConfiguration().getBucket(), zkProfileOperations, isZkEnabled),
new ProfileController(rateLimiters, accountsManager, profilesManager, usernamesManager, dynamicConfigurationManager, profileBadgeConverter, cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner, config.getCdnConfiguration().getBucket(), zkProfileOperations, isZkEnabled),
new ProvisioningController(rateLimiters, provisioningManager),
new RemoteConfigController(remoteConfigsManager, config.getRemoteConfigConfiguration().getAuthorizedTokens(), config.getRemoteConfigConfiguration().getGlobalConfig()),
new SecureBackupController(backupCredentialsGenerator),

View File

@ -0,0 +1,22 @@
/*
* Copyright 2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.badges;
import java.util.Set;
import javax.ws.rs.core.Request;
import org.whispersystems.textsecuregcm.entities.Badge;
import org.whispersystems.textsecuregcm.storage.AccountBadge;
public interface ProfileBadgeConverter {
/**
* Converts the {@link AccountBadge}s for an account into the objects
* that can be returned on a profile fetch. This requires returning
* localized strings given the user's locale which will be retrieved from
* the {@link Request} object passed in.
*/
Set<Badge> convert(Request request, Set<AccountBadge> accountBadges);
}

View File

@ -10,7 +10,6 @@ import io.dropwizard.auth.Auth;
import java.security.SecureRandom;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
@ -25,7 +24,9 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Request;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import org.apache.commons.codec.DecoderException;
@ -44,6 +45,7 @@ import org.whispersystems.textsecuregcm.auth.Anonymous;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessChecksum;
import org.whispersystems.textsecuregcm.badges.ProfileBadgeConverter;
import org.whispersystems.textsecuregcm.entities.CreateProfileRequest;
import org.whispersystems.textsecuregcm.entities.Profile;
import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes;
@ -74,6 +76,7 @@ public class ProfileController {
private final AccountsManager accountsManager;
private final UsernamesManager usernamesManager;
private final DynamicConfigurationManager dynamicConfigurationManager;
private final ProfileBadgeConverter profileBadgeConverter;
private final PolicySigner policySigner;
private final PostPolicyGenerator policyGenerator;
@ -83,23 +86,25 @@ public class ProfileController {
private final S3Client s3client;
private final String bucket;
public ProfileController(RateLimiters rateLimiters,
public ProfileController(
RateLimiters rateLimiters,
AccountsManager accountsManager,
ProfilesManager profilesManager,
UsernamesManager usernamesManager,
DynamicConfigurationManager dynamicConfigurationManager,
ProfileBadgeConverter profileBadgeConverter,
S3Client s3client,
PostPolicyGenerator policyGenerator,
PolicySigner policySigner,
String bucket,
ServerZkProfileOperations zkProfileOperations,
boolean isZkEnabled)
{
boolean isZkEnabled) {
this.rateLimiters = rateLimiters;
this.accountsManager = accountsManager;
this.profilesManager = profilesManager;
this.usernamesManager = usernamesManager;
this.dynamicConfigurationManager = dynamicConfigurationManager;
this.profileBadgeConverter = profileBadgeConverter;
this.zkProfileOperations = zkProfileOperations;
this.bucket = bucket;
this.s3client = s3client;
@ -175,23 +180,27 @@ public class ProfileController {
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/{uuid}/{version}")
public Optional<Profile> getProfile(@Auth Optional<AuthenticatedAccount> auth,
public Optional<Profile> getProfile(
@Auth Optional<AuthenticatedAccount> auth,
@HeaderParam(OptionalAccess.UNIDENTIFIED) Optional<Anonymous> accessKey,
@Context Request request,
@PathParam("uuid") UUID uuid,
@PathParam("version") String version)
throws RateLimitExceededException {
if (!isZkEnabled) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
return getVersionedProfile(auth.map(AuthenticatedAccount::getAccount), accessKey, uuid, version, Optional.empty());
return getVersionedProfile(auth.map(AuthenticatedAccount::getAccount), accessKey, request, uuid, version, Optional.empty());
}
@Timed
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/{uuid}/{version}/{credentialRequest}")
public Optional<Profile> getProfile(@Auth Optional<AuthenticatedAccount> auth,
public Optional<Profile> getProfile(
@Auth Optional<AuthenticatedAccount> auth,
@HeaderParam(OptionalAccess.UNIDENTIFIED) Optional<Anonymous> accessKey,
@Context Request request,
@PathParam("uuid") UUID uuid,
@PathParam("version") String version,
@PathParam("credentialRequest") String credentialRequest)
@ -199,17 +208,17 @@ public class ProfileController {
if (!isZkEnabled) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
return getVersionedProfile(auth.map(AuthenticatedAccount::getAccount), accessKey, uuid, version,
Optional.of(credentialRequest));
return getVersionedProfile(auth.map(AuthenticatedAccount::getAccount), accessKey, request, uuid, version, Optional.of(credentialRequest));
}
private Optional<Profile> getVersionedProfile(Optional<Account> requestAccount,
Optional<Anonymous> accessKey,
UUID uuid,
String version,
Optional<String> credentialRequest)
throws RateLimitExceededException
{
private Optional<Profile> getVersionedProfile(
Optional<Account> requestAccount,
Optional<Anonymous> accessKey,
Request request,
UUID uuid,
String version,
Optional<String> credentialRequest)
throws RateLimitExceededException {
if (!isZkEnabled) throw new WebApplicationException(Response.Status.NOT_FOUND);
try {
@ -244,7 +253,8 @@ public class ProfileController {
Optional<ProfileKeyCredentialResponse> credential = getProfileCredential(credentialRequest, profile, uuid);
return Optional.of(new Profile(name,
return Optional.of(new Profile(
name,
about,
aboutEmoji,
avatar,
@ -255,7 +265,7 @@ public class ProfileController {
UserCapabilities.createForAccount(accountProfile.get()),
username.orElse(null),
null,
List.of(),
profileBadgeConverter.convert(request, accountProfile.get().getBadges()),
credential.orElse(null)));
} catch (InvalidInputException e) {
logger.info("Bad profile request", e);
@ -298,7 +308,7 @@ public class ProfileController {
UserCapabilities.createForAccount(accountProfile.get()),
username,
accountProfile.get().getUuid(),
List.of(),
Set.of(),
null);
}
@ -371,7 +381,7 @@ public class ProfileController {
UserCapabilities.createForAccount(accountProfile.get()),
username.orElse(null),
null,
List.of(),
Set.of(),
null);
}

View File

@ -9,7 +9,7 @@ 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.Set;
import java.util.UUID;
import org.signal.zkgroup.profiles.ProfileKeyCredentialResponse;
@ -49,7 +49,7 @@ public class Profile {
private UUID uuid;
@JsonProperty
private List<Badge> badges;
private Set<Badge> badges;
@JsonProperty
@JsonSerialize(using = ProfileKeyCredentialResponseAdapter.Serializing.class)
@ -61,7 +61,7 @@ public class 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, final List<Badge> badges, ProfileKeyCredentialResponse credential)
UUID uuid, Set<Badge> badges, ProfileKeyCredentialResponse credential)
{
this.name = name;
this.about = about;
@ -130,7 +130,7 @@ public class Profile {
return uuid;
}
public List<Badge> getBadges() {
public Set<Badge> getBadges() {
return badges;
}
}

View File

@ -90,11 +90,13 @@ class ProfileControllerTest {
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.setMapper(SystemMapper.getMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new ProfileController(rateLimiters,
.addResource(new ProfileController(
rateLimiters,
accountsManager,
profilesManager,
usernamesManager,
dynamicConfigurationManager,
(request, accountBadges) -> Set.of(), // TODO: Test with some badges.
s3client,
postPolicyGenerator,
policySigner,