Wire up stored account badges to the profile endpoints
This commit is contained in:
parent
bc887ec6fa
commit
fc1465c05d
|
@ -43,6 +43,7 @@ import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.ExecutorService;
|
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.DisabledPermittedAuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialGenerator;
|
||||||
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
|
||||||
|
import org.whispersystems.textsecuregcm.badges.ProfileBadgeConverter;
|
||||||
import org.whispersystems.textsecuregcm.configuration.DirectoryServerConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.DirectoryServerConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.controllers.AccountController;
|
import org.whispersystems.textsecuregcm.controllers.AccountController;
|
||||||
import org.whispersystems.textsecuregcm.controllers.AttachmentControllerV1;
|
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.ALL, JsonAutoDetect.Visibility.NONE);
|
||||||
environment.getObjectMapper().setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
|
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);
|
JdbiFactory jdbiFactory = new JdbiFactory(DefaultNameStrategy.CHECK_EMPTY);
|
||||||
Jdbi accountJdbi = jdbiFactory.build(environment, config.getAccountsDatabaseConfiguration(), "accountdb");
|
Jdbi accountJdbi = jdbiFactory.build(environment, config.getAccountsDatabaseConfiguration(), "accountdb");
|
||||||
Jdbi abuseJdbi = jdbiFactory.build(environment, config.getAbuseDatabaseConfiguration(), "abusedb" );
|
Jdbi abuseJdbi = jdbiFactory.build(environment, config.getAbuseDatabaseConfiguration(), "abusedb" );
|
||||||
|
@ -586,7 +590,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
new DonationController(donationExecutor, config.getDonationConfiguration()),
|
new DonationController(donationExecutor, config.getDonationConfiguration()),
|
||||||
new MessageController(rateLimiters, messageSender, receiptSender, accountsManager, messagesManager, unsealedSenderRateLimiter, apnFallbackManager, dynamicConfigurationManager, rateLimitChallengeManager, reportMessageManager, metricsCluster, declinedMessageReceiptExecutor, multiRecipientMessageExecutor),
|
new MessageController(rateLimiters, messageSender, receiptSender, accountsManager, messagesManager, unsealedSenderRateLimiter, apnFallbackManager, dynamicConfigurationManager, rateLimitChallengeManager, reportMessageManager, metricsCluster, declinedMessageReceiptExecutor, multiRecipientMessageExecutor),
|
||||||
new PaymentsController(currencyManager, paymentsCredentialsGenerator),
|
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 ProvisioningController(rateLimiters, provisioningManager),
|
||||||
new RemoteConfigController(remoteConfigsManager, config.getRemoteConfigConfiguration().getAuthorizedTokens(), config.getRemoteConfigConfiguration().getGlobalConfig()),
|
new RemoteConfigController(remoteConfigsManager, config.getRemoteConfigConfiguration().getAuthorizedTokens(), config.getRemoteConfigConfiguration().getGlobalConfig()),
|
||||||
new SecureBackupController(backupCredentialsGenerator),
|
new SecureBackupController(backupCredentialsGenerator),
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -10,7 +10,6 @@ import io.dropwizard.auth.Auth;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.time.ZoneOffset;
|
import java.time.ZoneOffset;
|
||||||
import java.time.ZonedDateTime;
|
import java.time.ZonedDateTime;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
@ -25,7 +24,9 @@ import javax.ws.rs.PathParam;
|
||||||
import javax.ws.rs.Produces;
|
import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.QueryParam;
|
import javax.ws.rs.QueryParam;
|
||||||
import javax.ws.rs.WebApplicationException;
|
import javax.ws.rs.WebApplicationException;
|
||||||
|
import javax.ws.rs.core.Context;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
|
import javax.ws.rs.core.Request;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
import org.apache.commons.codec.DecoderException;
|
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.AuthenticatedAccount;
|
||||||
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
|
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
|
||||||
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessChecksum;
|
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessChecksum;
|
||||||
|
import org.whispersystems.textsecuregcm.badges.ProfileBadgeConverter;
|
||||||
import org.whispersystems.textsecuregcm.entities.CreateProfileRequest;
|
import org.whispersystems.textsecuregcm.entities.CreateProfileRequest;
|
||||||
import org.whispersystems.textsecuregcm.entities.Profile;
|
import org.whispersystems.textsecuregcm.entities.Profile;
|
||||||
import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes;
|
import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes;
|
||||||
|
@ -74,6 +76,7 @@ public class ProfileController {
|
||||||
private final AccountsManager accountsManager;
|
private final AccountsManager accountsManager;
|
||||||
private final UsernamesManager usernamesManager;
|
private final UsernamesManager usernamesManager;
|
||||||
private final DynamicConfigurationManager dynamicConfigurationManager;
|
private final DynamicConfigurationManager dynamicConfigurationManager;
|
||||||
|
private final ProfileBadgeConverter profileBadgeConverter;
|
||||||
|
|
||||||
private final PolicySigner policySigner;
|
private final PolicySigner policySigner;
|
||||||
private final PostPolicyGenerator policyGenerator;
|
private final PostPolicyGenerator policyGenerator;
|
||||||
|
@ -83,23 +86,25 @@ public class ProfileController {
|
||||||
private final S3Client s3client;
|
private final S3Client s3client;
|
||||||
private final String bucket;
|
private final String bucket;
|
||||||
|
|
||||||
public ProfileController(RateLimiters rateLimiters,
|
public ProfileController(
|
||||||
|
RateLimiters rateLimiters,
|
||||||
AccountsManager accountsManager,
|
AccountsManager accountsManager,
|
||||||
ProfilesManager profilesManager,
|
ProfilesManager profilesManager,
|
||||||
UsernamesManager usernamesManager,
|
UsernamesManager usernamesManager,
|
||||||
DynamicConfigurationManager dynamicConfigurationManager,
|
DynamicConfigurationManager dynamicConfigurationManager,
|
||||||
|
ProfileBadgeConverter profileBadgeConverter,
|
||||||
S3Client s3client,
|
S3Client s3client,
|
||||||
PostPolicyGenerator policyGenerator,
|
PostPolicyGenerator policyGenerator,
|
||||||
PolicySigner policySigner,
|
PolicySigner policySigner,
|
||||||
String bucket,
|
String bucket,
|
||||||
ServerZkProfileOperations zkProfileOperations,
|
ServerZkProfileOperations zkProfileOperations,
|
||||||
boolean isZkEnabled)
|
boolean isZkEnabled) {
|
||||||
{
|
|
||||||
this.rateLimiters = rateLimiters;
|
this.rateLimiters = rateLimiters;
|
||||||
this.accountsManager = accountsManager;
|
this.accountsManager = accountsManager;
|
||||||
this.profilesManager = profilesManager;
|
this.profilesManager = profilesManager;
|
||||||
this.usernamesManager = usernamesManager;
|
this.usernamesManager = usernamesManager;
|
||||||
this.dynamicConfigurationManager = dynamicConfigurationManager;
|
this.dynamicConfigurationManager = dynamicConfigurationManager;
|
||||||
|
this.profileBadgeConverter = profileBadgeConverter;
|
||||||
this.zkProfileOperations = zkProfileOperations;
|
this.zkProfileOperations = zkProfileOperations;
|
||||||
this.bucket = bucket;
|
this.bucket = bucket;
|
||||||
this.s3client = s3client;
|
this.s3client = s3client;
|
||||||
|
@ -175,23 +180,27 @@ public class ProfileController {
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@Path("/{uuid}/{version}")
|
@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,
|
@HeaderParam(OptionalAccess.UNIDENTIFIED) Optional<Anonymous> accessKey,
|
||||||
|
@Context Request request,
|
||||||
@PathParam("uuid") UUID uuid,
|
@PathParam("uuid") UUID uuid,
|
||||||
@PathParam("version") String version)
|
@PathParam("version") String version)
|
||||||
throws RateLimitExceededException {
|
throws RateLimitExceededException {
|
||||||
if (!isZkEnabled) {
|
if (!isZkEnabled) {
|
||||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
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
|
@Timed
|
||||||
@GET
|
@GET
|
||||||
@Produces(MediaType.APPLICATION_JSON)
|
@Produces(MediaType.APPLICATION_JSON)
|
||||||
@Path("/{uuid}/{version}/{credentialRequest}")
|
@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,
|
@HeaderParam(OptionalAccess.UNIDENTIFIED) Optional<Anonymous> accessKey,
|
||||||
|
@Context Request request,
|
||||||
@PathParam("uuid") UUID uuid,
|
@PathParam("uuid") UUID uuid,
|
||||||
@PathParam("version") String version,
|
@PathParam("version") String version,
|
||||||
@PathParam("credentialRequest") String credentialRequest)
|
@PathParam("credentialRequest") String credentialRequest)
|
||||||
|
@ -199,17 +208,17 @@ public class ProfileController {
|
||||||
if (!isZkEnabled) {
|
if (!isZkEnabled) {
|
||||||
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||||
}
|
}
|
||||||
return getVersionedProfile(auth.map(AuthenticatedAccount::getAccount), accessKey, uuid, version,
|
return getVersionedProfile(auth.map(AuthenticatedAccount::getAccount), accessKey, request, uuid, version, Optional.of(credentialRequest));
|
||||||
Optional.of(credentialRequest));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Optional<Profile> getVersionedProfile(Optional<Account> requestAccount,
|
private Optional<Profile> getVersionedProfile(
|
||||||
Optional<Anonymous> accessKey,
|
Optional<Account> requestAccount,
|
||||||
UUID uuid,
|
Optional<Anonymous> accessKey,
|
||||||
String version,
|
Request request,
|
||||||
Optional<String> credentialRequest)
|
UUID uuid,
|
||||||
throws RateLimitExceededException
|
String version,
|
||||||
{
|
Optional<String> credentialRequest)
|
||||||
|
throws RateLimitExceededException {
|
||||||
if (!isZkEnabled) throw new WebApplicationException(Response.Status.NOT_FOUND);
|
if (!isZkEnabled) throw new WebApplicationException(Response.Status.NOT_FOUND);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -244,7 +253,8 @@ public class ProfileController {
|
||||||
|
|
||||||
Optional<ProfileKeyCredentialResponse> credential = getProfileCredential(credentialRequest, profile, uuid);
|
Optional<ProfileKeyCredentialResponse> credential = getProfileCredential(credentialRequest, profile, uuid);
|
||||||
|
|
||||||
return Optional.of(new Profile(name,
|
return Optional.of(new Profile(
|
||||||
|
name,
|
||||||
about,
|
about,
|
||||||
aboutEmoji,
|
aboutEmoji,
|
||||||
avatar,
|
avatar,
|
||||||
|
@ -255,7 +265,7 @@ public class ProfileController {
|
||||||
UserCapabilities.createForAccount(accountProfile.get()),
|
UserCapabilities.createForAccount(accountProfile.get()),
|
||||||
username.orElse(null),
|
username.orElse(null),
|
||||||
null,
|
null,
|
||||||
List.of(),
|
profileBadgeConverter.convert(request, accountProfile.get().getBadges()),
|
||||||
credential.orElse(null)));
|
credential.orElse(null)));
|
||||||
} catch (InvalidInputException e) {
|
} catch (InvalidInputException e) {
|
||||||
logger.info("Bad profile request", e);
|
logger.info("Bad profile request", e);
|
||||||
|
@ -298,7 +308,7 @@ public class ProfileController {
|
||||||
UserCapabilities.createForAccount(accountProfile.get()),
|
UserCapabilities.createForAccount(accountProfile.get()),
|
||||||
username,
|
username,
|
||||||
accountProfile.get().getUuid(),
|
accountProfile.get().getUuid(),
|
||||||
List.of(),
|
Set.of(),
|
||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,7 +381,7 @@ public class ProfileController {
|
||||||
UserCapabilities.createForAccount(accountProfile.get()),
|
UserCapabilities.createForAccount(accountProfile.get()),
|
||||||
username.orElse(null),
|
username.orElse(null),
|
||||||
null,
|
null,
|
||||||
List.of(),
|
Set.of(),
|
||||||
null);
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ 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 com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
import java.util.List;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import org.signal.zkgroup.profiles.ProfileKeyCredentialResponse;
|
import org.signal.zkgroup.profiles.ProfileKeyCredentialResponse;
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ public class Profile {
|
||||||
private UUID uuid;
|
private UUID uuid;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private List<Badge> badges;
|
private Set<Badge> badges;
|
||||||
|
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
@JsonSerialize(using = ProfileKeyCredentialResponseAdapter.Serializing.class)
|
@JsonSerialize(using = ProfileKeyCredentialResponseAdapter.Serializing.class)
|
||||||
|
@ -61,7 +61,7 @@ public class Profile {
|
||||||
public Profile(
|
public Profile(
|
||||||
String name, String about, String aboutEmoji, String avatar, String paymentAddress, String identityKey,
|
String name, String about, String aboutEmoji, String avatar, String paymentAddress, String identityKey,
|
||||||
String unidentifiedAccess, boolean unrestrictedUnidentifiedAccess, UserCapabilities capabilities, String username,
|
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.name = name;
|
||||||
this.about = about;
|
this.about = about;
|
||||||
|
@ -130,7 +130,7 @@ public class Profile {
|
||||||
return uuid;
|
return uuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Badge> getBadges() {
|
public Set<Badge> getBadges() {
|
||||||
return badges;
|
return badges;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,11 +90,13 @@ class ProfileControllerTest {
|
||||||
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
|
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
|
||||||
.setMapper(SystemMapper.getMapper())
|
.setMapper(SystemMapper.getMapper())
|
||||||
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
|
||||||
.addResource(new ProfileController(rateLimiters,
|
.addResource(new ProfileController(
|
||||||
|
rateLimiters,
|
||||||
accountsManager,
|
accountsManager,
|
||||||
profilesManager,
|
profilesManager,
|
||||||
usernamesManager,
|
usernamesManager,
|
||||||
dynamicConfigurationManager,
|
dynamicConfigurationManager,
|
||||||
|
(request, accountBadges) -> Set.of(), // TODO: Test with some badges.
|
||||||
s3client,
|
s3client,
|
||||||
postPolicyGenerator,
|
postPolicyGenerator,
|
||||||
policySigner,
|
policySigner,
|
||||||
|
|
Loading…
Reference in New Issue