Return a badge with additional properties when fetching your own profile

This commit is contained in:
Ehren Kret
2021-09-17 13:20:13 -05:00
parent 5c1cde1b28
commit 44bc90e5ab
6 changed files with 138 additions and 34 deletions

View File

@@ -6,7 +6,9 @@
package org.whispersystems.textsecuregcm.badges;
import com.google.common.annotations.VisibleForTesting;
import java.net.URL;
import java.time.Clock;
import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@@ -20,6 +22,7 @@ import java.util.stream.Collectors;
import org.whispersystems.textsecuregcm.configuration.BadgeConfiguration;
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
import org.whispersystems.textsecuregcm.entities.Badge;
import org.whispersystems.textsecuregcm.entities.SelfBadge;
import org.whispersystems.textsecuregcm.storage.AccountBadge;
public class ConfiguredProfileBadgeConverter implements ProfileBadgeConverter {
@@ -54,7 +57,8 @@ public class ConfiguredProfileBadgeConverter implements ProfileBadgeConverter {
@Override
public List<Badge> convert(
final List<Locale> acceptableLanguages,
final List<AccountBadge> accountBadges) {
final List<AccountBadge> accountBadges,
final boolean isSelf) {
if (accountBadges.isEmpty() && badgeIdsEnabledForAll.isEmpty()) {
return List.of();
}
@@ -95,23 +99,45 @@ public class ConfiguredProfileBadgeConverter implements ProfileBadgeConverter {
&& knownBadges.containsKey(accountBadge.getId()))
.map(accountBadge -> {
BadgeConfiguration configuration = knownBadges.get(accountBadge.getId());
return new Badge(
return newBadge(
isSelf,
accountBadge.getId(),
configuration.getCategory(),
configuration.getImageUrl(),
resourceBundle.getString(accountBadge.getId() + "_name"),
resourceBundle.getString(accountBadge.getId() + "_description"));
resourceBundle.getString(accountBadge.getId() + "_description"),
accountBadge.getExpiration(),
accountBadge.isVisible());
})
.collect(Collectors.toCollection(ArrayList::new));
badges.addAll(badgeIdsEnabledForAll.stream().filter(knownBadges::containsKey).map(id -> {
BadgeConfiguration configuration = knownBadges.get(id);
return new Badge(
return newBadge(
isSelf,
id,
configuration.getCategory(),
configuration.getImageUrl(),
resourceBundle.getString(id + "_name"),
resourceBundle.getString(id + "_description"));
resourceBundle.getString(id + "_description"),
now.plus(Duration.ofDays(1)),
true);
}).collect(Collectors.toList()));
return badges;
}
private Badge newBadge(
final boolean isSelf,
final String id,
final String category,
final URL imageUrl,
final String name,
final String description,
final Instant expiration,
final boolean visible) {
if (isSelf) {
return new SelfBadge(id, category, imageUrl, name, description, expiration, visible);
} else {
return new Badge(id, category, imageUrl, name, description);
}
}
}

View File

@@ -16,5 +16,5 @@ public interface ProfileBadgeConverter {
* Converts the {@link AccountBadge}s for an account into the objects
* that can be returned on a profile fetch.
*/
List<Badge> convert(List<Locale> acceptableLanguages, List<AccountBadge> accountBadges);
List<Badge> convert(List<Locale> acceptableLanguages, List<AccountBadge> accountBadges, boolean isSelf);
}

View File

@@ -243,8 +243,11 @@ public class ProfileController {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
boolean isSelf = false;
if (requestAccount.isPresent()) {
rateLimiters.getProfileLimiter().validate(requestAccount.get().getUuid());
UUID authedUuid = requestAccount.get().getUuid();
rateLimiters.getProfileLimiter().validate(authedUuid);
isSelf = uuid.equals(authedUuid);
}
Optional<Account> accountProfile = accountsManager.get(uuid);
@@ -282,7 +285,7 @@ public class ProfileController {
UserCapabilities.createForAccount(accountProfile.get()),
username.orElse(null),
null,
profileBadgeConverter.convert(acceptableLanguages, accountProfile.get().getBadges()),
profileBadgeConverter.convert(acceptableLanguages, accountProfile.get().getBadges(), isSelf),
credential.orElse(null)));
} catch (InvalidInputException e) {
logger.info("Bad profile request", e);
@@ -291,26 +294,28 @@ public class ProfileController {
}
@Timed
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/username/{username}")
public Profile getProfileByUsername(
@Auth AuthenticatedAccount auth,
@Context ContainerRequestContext containerRequestContext,
@PathParam("username") String username)
throws RateLimitExceededException {
rateLimiters.getUsernameLookupLimiter().validate(auth.getAccount().getUuid());
@Timed
@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/username/{username}")
public Profile getProfileByUsername(
@Auth AuthenticatedAccount auth,
@Context ContainerRequestContext containerRequestContext,
@PathParam("username") String username)
throws RateLimitExceededException {
rateLimiters.getUsernameLookupLimiter().validate(auth.getAccount().getUuid());
username = username.toLowerCase();
username = username.toLowerCase();
Optional<UUID> uuid = usernamesManager.get(username);
Optional<UUID> uuid = usernamesManager.get(username);
if (uuid.isEmpty()) {
throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build());
}
if (uuid.isEmpty()) {
throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build());
}
Optional<Account> accountProfile = accountsManager.get(uuid.get());
final boolean isSelf = auth.getAccount().getUuid().equals(uuid.get());
Optional<Account> accountProfile = accountsManager.get(uuid.get());
if (accountProfile.isEmpty()) {
throw new WebApplicationException(Response.status(Response.Status.NOT_FOUND).build());
@@ -328,7 +333,10 @@ public class ProfileController {
UserCapabilities.createForAccount(accountProfile.get()),
username,
accountProfile.get().getUuid(),
profileBadgeConverter.convert(getAcceptableLanguagesForRequest(containerRequestContext), accountProfile.get().getBadges()),
profileBadgeConverter.convert(
getAcceptableLanguagesForRequest(containerRequestContext),
accountProfile.get().getBadges(),
isSelf),
null);
}
@@ -382,8 +390,11 @@ public class ProfileController {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
boolean isSelf = false;
if (auth.isPresent()) {
rateLimiters.getProfileLimiter().validate(auth.get().getAccount().getUuid());
UUID authedUuid = auth.get().getAccount().getUuid();
rateLimiters.getProfileLimiter().validate(authedUuid);
isSelf = authedUuid.equals(identifier);
}
Optional<Account> accountProfile = accountsManager.get(identifier);
@@ -403,11 +414,13 @@ public class ProfileController {
UserCapabilities.createForAccount(accountProfile.get()),
username.orElse(null),
null,
profileBadgeConverter.convert(getAcceptableLanguagesForRequest(containerRequestContext), accountProfile.get().getBadges()),
profileBadgeConverter.convert(
getAcceptableLanguagesForRequest(containerRequestContext),
accountProfile.get().getBadges(),
isSelf),
null);
}
@Deprecated
@Timed
@GET

View File

@@ -0,0 +1,63 @@
/*
* Copyright 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 java.net.URL;
import java.time.Instant;
import java.util.Objects;
/**
* Extension of the Badge object returned when asking for one's own badges.
*/
public class SelfBadge extends Badge {
private final Instant expiration;
private final boolean visible;
@JsonCreator
public SelfBadge(
@JsonProperty("id") final String id,
@JsonProperty("category") final String category,
@JsonProperty("imageUrl") final URL imageUrl,
@JsonProperty("name") final String name,
@JsonProperty("description") final String description,
@JsonProperty("expiration") final Instant expiration,
@JsonProperty("visible") final boolean visible) {
super(id, category, imageUrl, name, description);
this.expiration = expiration;
this.visible = visible;
}
public Instant getExpiration() {
return expiration;
}
public boolean isVisible() {
return visible;
}
@Override
public boolean equals(final Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
if (!super.equals(o)) {
return false;
}
SelfBadge selfBadge = (SelfBadge) o;
return visible == selfBadge.visible && Objects.equals(expiration, selfBadge.expiration);
}
@Override
public int hashCode() {
return Objects.hash(super.hashCode(), expiration, visible);
}
}