diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index 6870f213a..dbf45bf2b 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -29,7 +29,6 @@ import io.dropwizard.jdbi3.JdbiFactory; import io.dropwizard.setup.Bootstrap; import io.dropwizard.setup.Environment; import io.lettuce.core.resource.ClientResources; -import io.micrometer.core.instrument.Clock; import io.micrometer.core.instrument.Meter.Id; import io.micrometer.core.instrument.Metrics; import io.micrometer.core.instrument.Tags; @@ -37,6 +36,7 @@ import io.micrometer.core.instrument.config.MeterFilter; import io.micrometer.core.instrument.distribution.DistributionStatisticConfig; import io.micrometer.datadog.DatadogMeterRegistry; import java.net.http.HttpClient; +import java.time.Clock; import java.time.Duration; import java.util.ArrayList; import java.util.Collections; @@ -69,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.ConfiguredProfileBadgeConverter; import org.whispersystems.textsecuregcm.badges.ProfileBadgeConverter; import org.whispersystems.textsecuregcm.configuration.DirectoryServerConfiguration; import org.whispersystems.textsecuregcm.controllers.AccountController; @@ -269,7 +270,8 @@ public class WhisperServerService extends Application List.of(); // TODO: Provide an actual implementation. + ProfileBadgeConverter profileBadgeConverter = new ConfiguredProfileBadgeConverter( + Clock.systemUTC(), config.getBadges()); JdbiFactory jdbiFactory = new JdbiFactory(DefaultNameStrategy.CHECK_EMPTY); Jdbi accountJdbi = jdbiFactory.build(environment, config.getAccountsDatabaseConfiguration(), "accountdb"); 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 a0c0e5045..21b22b158 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/controllers/ProfileController.java @@ -296,8 +296,11 @@ public class ProfileController { @GET @Produces(MediaType.APPLICATION_JSON) @Path("/username/{username}") - public Profile getProfileByUsername(@Auth AuthenticatedAccount auth, @PathParam("username") String username) - throws RateLimitExceededException { + public Profile getProfileByUsername( + @Auth AuthenticatedAccount auth, + @Context ContainerRequestContext containerRequestContext, + @PathParam("username") String username) + throws RateLimitExceededException { rateLimiters.getUsernameLookupLimiter().validate(auth.getAccount().getUuid()); username = username.toLowerCase(); @@ -326,7 +329,7 @@ public class ProfileController { UserCapabilities.createForAccount(accountProfile.get()), username, accountProfile.get().getUuid(), - List.of(), + profileBadgeConverter.convert(containerRequestContext.getAcceptableLanguages(), accountProfile.get().getBadges()), null); } @@ -367,8 +370,10 @@ public class ProfileController { @GET @Produces(MediaType.APPLICATION_JSON) @Path("/{identifier}") - public Profile getProfile(@Auth Optional auth, + public Profile getProfile( + @Auth Optional auth, @HeaderParam(OptionalAccess.UNIDENTIFIED) Optional accessKey, + @Context ContainerRequestContext containerRequestContext, @HeaderParam("User-Agent") String userAgent, @PathParam("identifier") UUID identifier, @QueryParam("ca") boolean useCaCertificate) @@ -399,7 +404,7 @@ public class ProfileController { UserCapabilities.createForAccount(accountProfile.get()), username.orElse(null), null, - List.of(), + profileBadgeConverter.convert(containerRequestContext.getAcceptableLanguages(), accountProfile.get().getBadges()), null); } 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 3fd8ba668..44fc75954 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 @@ -21,6 +21,8 @@ import com.google.common.collect.ImmutableSet; import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.ResourceExtension; +import java.net.MalformedURLException; +import java.net.URL; import java.util.Collections; import java.util.List; import java.util.Optional; @@ -29,6 +31,7 @@ import javax.ws.rs.client.Entity; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import org.apache.commons.lang3.RandomStringUtils; +import org.assertj.core.api.Condition; import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -45,6 +48,7 @@ import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfigurati import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicPaymentsConfiguration; import org.whispersystems.textsecuregcm.controllers.ProfileController; import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException; +import org.whispersystems.textsecuregcm.entities.Badge; import org.whispersystems.textsecuregcm.entities.CreateProfileRequest; import org.whispersystems.textsecuregcm.entities.Profile; import org.whispersystems.textsecuregcm.entities.ProfileAvatarUploadAttributes; @@ -97,7 +101,15 @@ class ProfileControllerTest { profilesManager, usernamesManager, dynamicConfigurationManager, - (acceptableLanguages, accountBadges) -> List.of(), // TODO: Test with some badges. + (acceptableLanguages, accountBadges) -> { + try { + return List.of( + new Badge(new URL("https://example.com/badge/1"), "Test Badge", "This badge is in unit tests.") + ); + } catch (MalformedURLException e) { + throw new AssertionError(e); + } + }, s3client, postPolicyGenerator, policySigner, @@ -184,6 +196,8 @@ class ProfileControllerTest { assertThat(profile.getName()).isEqualTo("baz"); assertThat(profile.getAvatar()).isEqualTo("profiles/bang"); assertThat(profile.getUsername()).isEqualTo("n00bkiller"); + assertThat(profile.getBadges()).hasSize(1).element(0).has(new Condition<>( + badge -> "Test Badge".equals(badge.getName()), "has badge with expected name")); verify(accountsManager).get(AuthHelper.VALID_UUID_TWO); verify(usernamesManager, times(1)).get(eq(AuthHelper.VALID_UUID_TWO)); @@ -203,6 +217,8 @@ class ProfileControllerTest { assertThat(profile.getAvatar()).isEqualTo("profiles/bang"); assertThat(profile.getUsername()).isEqualTo("n00bkiller"); assertThat(profile.getUuid()).isEqualTo(AuthHelper.VALID_UUID_TWO); + assertThat(profile.getBadges()).hasSize(1).element(0).has(new Condition<>( + badge -> "Test Badge".equals(badge.getName()), "has badge with expected name")); verify(accountsManager, times(1)).get(eq(AuthHelper.VALID_UUID_TWO)); verify(usernamesManager, times(1)).get(eq("n00bkiller")); @@ -563,6 +579,8 @@ class ProfileControllerTest { assertThat(profile.getCapabilities().isGv1Migration()).isFalse(); assertThat(profile.getUsername()).isEqualTo("n00bkiller"); assertThat(profile.getUuid()).isNull(); + assertThat(profile.getBadges()).hasSize(1).element(0).has(new Condition<>( + badge -> "Test Badge".equals(badge.getName()), "has badge with expected name")); verify(accountsManager, times(1)).get(eq(AuthHelper.VALID_UUID_TWO)); verify(usernamesManager, times(1)).get(eq(AuthHelper.VALID_UUID_TWO));