Skip to content

Commit

Permalink
Remove PUT/DELETE methods from RemoteConfigController
Browse files Browse the repository at this point in the history
  • Loading branch information
eager-signal authored and jon-signal committed Dec 7, 2023
1 parent 664f9f3 commit e084a9f
Show file tree
Hide file tree
Showing 6 changed files with 15 additions and 370 deletions.
11 changes: 0 additions & 11 deletions service/config/sample.yml
Original file line number Diff line number Diff line change
Expand Up @@ -337,17 +337,6 @@ appConfig:
configuration: example

remoteConfig:
authorizedUsers:
- # 1st authorized user
- # 2nd authorized user
- # ...
- # Nth authorized user
requiredHostedDomain: example.com
audiences:
- # 1st audience
- # 2nd audience
- # ...
- # Nth audience
globalConfig: # keys and values that are given to clients on GET /v1/config
EXAMPLE_KEY: VALUE

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@
import static com.codahale.metrics.MetricRegistry.name;
import static java.util.Objects.requireNonNull;

import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.api.client.http.apache.v2.ApacheHttpTransport;
import com.google.api.client.json.gson.GsonFactory;
import com.google.auth.oauth2.GoogleCredentials;
import com.google.cloud.logging.LoggingOptions;
import com.google.common.collect.ImmutableMap;
Expand Down Expand Up @@ -808,14 +805,20 @@ public void run(WhisperServerConfiguration config, Environment environment) thro
new AccountControllerV2(accountsManager, changeNumberManager, phoneVerificationTokenManager,
registrationLockVerificationManager, rateLimiters),
new ArtController(rateLimiters, artCredentialsGenerator),
new AttachmentControllerV2(rateLimiters, config.getAwsAttachmentsConfiguration().accessKey().value(), config.getAwsAttachmentsConfiguration().accessSecret().value(), config.getAwsAttachmentsConfiguration().region(), config.getAwsAttachmentsConfiguration().bucket()),
new AttachmentControllerV2(rateLimiters, config.getAwsAttachmentsConfiguration().accessKey().value(),
config.getAwsAttachmentsConfiguration().accessSecret().value(),
config.getAwsAttachmentsConfiguration().region(), config.getAwsAttachmentsConfiguration().bucket()),
new AttachmentControllerV3(rateLimiters, gcsAttachmentGenerator),
new AttachmentControllerV4(rateLimiters, gcsAttachmentGenerator, new TusAttachmentGenerator(config.getTus()), experimentEnrollmentManager),
new AttachmentControllerV4(rateLimiters, gcsAttachmentGenerator, new TusAttachmentGenerator(config.getTus()),
experimentEnrollmentManager),
new ArchiveController(backupAuthManager, backupManager),
new CallLinkController(rateLimiters, callingGenericZkSecretParams),
new CertificateController(new CertificateGenerator(config.getDeliveryCertificate().certificate().value(), config.getDeliveryCertificate().ecPrivateKey(), config.getDeliveryCertificate().expiresDays()), zkAuthOperations, callingGenericZkSecretParams, clock),
new CertificateController(new CertificateGenerator(config.getDeliveryCertificate().certificate().value(),
config.getDeliveryCertificate().ecPrivateKey(), config.getDeliveryCertificate().expiresDays()),
zkAuthOperations, callingGenericZkSecretParams, clock),
new ChallengeController(rateLimitChallengeManager, useRemoteAddress),
new DeviceController(config.getLinkDeviceSecretConfiguration().secret().value(), accountsManager, messagesManager, keysManager, rateLimiters,
new DeviceController(config.getLinkDeviceSecretConfiguration().secret().value(), accountsManager,
messagesManager, keysManager, rateLimiters,
rateLimitersCluster, config.getMaxDevices(), clock),
new DirectoryV2Controller(directoryV2CredentialsGenerator),
new DonationController(clock, zkReceiptOperations, redeemedReceiptsManager, accountsManager, config.getBadges(),
Expand All @@ -831,13 +834,7 @@ public void run(WhisperServerConfiguration config, Environment environment) thro
new ProvisioningController(rateLimiters, provisioningManager),
new RegistrationController(accountsManager, phoneVerificationTokenManager, registrationLockVerificationManager,
rateLimiters),
new RemoteConfigController(remoteConfigsManager, adminEventLogger,
config.getRemoteConfigConfiguration().authorizedUsers(),
config.getRemoteConfigConfiguration().requiredHostedDomain(),
config.getRemoteConfigConfiguration().audiences(),
new GoogleIdTokenVerifier.Builder(new ApacheHttpTransport(), new GsonFactory()),
config.getRemoteConfigConfiguration().globalConfig(),
clock),
new RemoteConfigController(remoteConfigsManager, config.getRemoteConfigConfiguration().globalConfig(), clock),
new SecureStorageController(storageCredentialsGenerator),
new SecureValueRecovery2Controller(svr2CredentialsGenerator, accountsManager),
new SecureValueRecovery3Controller(svr3CredentialsGenerator, accountsManager),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,9 @@

package org.whispersystems.textsecuregcm.configuration;

import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;

public record RemoteConfigConfiguration(@NotNull Set<String> authorizedUsers,
@NotNull String requiredHostedDomain,
@NotNull @NotEmpty List<String> audiences,
@NotNull Map<String, String> globalConfig) {
public record RemoteConfigConfiguration(@NotNull Map<String, String> globalConfig) {

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@

package org.whispersystems.textsecuregcm.controllers;

import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.common.annotations.VisibleForTesting;
import io.dropwizard.auth.Auth;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -15,35 +13,18 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.Clock;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.signal.event.AdminEventLogger;
import org.signal.event.RemoteConfigDeleteEvent;
import org.signal.event.RemoteConfigSetEvent;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.entities.UserRemoteConfig;
import org.whispersystems.textsecuregcm.entities.UserRemoteConfigList;
import org.whispersystems.textsecuregcm.storage.RemoteConfig;
import org.whispersystems.textsecuregcm.storage.RemoteConfigsManager;
import org.whispersystems.textsecuregcm.util.Conversions;
import org.whispersystems.textsecuregcm.util.Util;
Expand All @@ -53,30 +34,18 @@
public class RemoteConfigController {

private final RemoteConfigsManager remoteConfigsManager;
private final AdminEventLogger adminEventLogger;
private final Set<String> configAuthUsers;
private final Map<String, String> globalConfig;

private final String requiredHostedDomain;

private final GoogleIdTokenVerifier googleIdTokenVerifier;

private final Clock clock;

private static final String GLOBAL_CONFIG_PREFIX = "global.";

public RemoteConfigController(RemoteConfigsManager remoteConfigsManager, AdminEventLogger adminEventLogger,
Set<String> configAuthUsers, String requiredHostedDomain, List<String> audience,
final GoogleIdTokenVerifier.Builder googleIdTokenVerifierBuilder, Map<String, String> globalConfig,
public RemoteConfigController(RemoteConfigsManager remoteConfigsManager,
Map<String, String> globalConfig,
final Clock clock) {
this.remoteConfigsManager = remoteConfigsManager;
this.adminEventLogger = Objects.requireNonNull(adminEventLogger);
this.configAuthUsers = configAuthUsers;
this.globalConfig = globalConfig;

this.requiredHostedDomain = requiredHostedDomain;
this.googleIdTokenVerifier = googleIdTokenVerifierBuilder.setAudience(audience).build();

this.clock = clock;
}

Expand All @@ -101,68 +70,6 @@ public UserRemoteConfigList getAll(@Auth AuthenticatedAccount auth) {
}
}

@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public void set(@HeaderParam("Config-Token") String configToken, @NotNull @Valid RemoteConfig config) {

final String authIdentity = getAuthIdentity(configToken)
.orElseThrow(() -> new WebApplicationException(Response.Status.UNAUTHORIZED));

if (config.getName().startsWith(GLOBAL_CONFIG_PREFIX)) {
throw new WebApplicationException(Response.Status.FORBIDDEN);
}

adminEventLogger.logEvent(
new RemoteConfigSetEvent(
authIdentity,
config.getName(),
config.getPercentage(),
config.getDefaultValue(),
config.getValue(),
config.getHashKey(),
config.getUuids().stream().map(UUID::toString).collect(Collectors.toList())));
remoteConfigsManager.set(config);
}

@DELETE
@Path("/{name}")
public void delete(@HeaderParam("Config-Token") String configToken, @PathParam("name") String name) {
final String authIdentity = getAuthIdentity(configToken)
.orElseThrow(() -> new WebApplicationException(Response.Status.UNAUTHORIZED));

if (name.startsWith(GLOBAL_CONFIG_PREFIX)) {
throw new WebApplicationException(Response.Status.FORBIDDEN);
}

adminEventLogger.logEvent(new RemoteConfigDeleteEvent(authIdentity, name));
remoteConfigsManager.delete(name);
}

private Optional<String> getAuthIdentity(String token) {
return getAuthorizedGoogleIdentity(token)
.map(googleIdToken -> googleIdToken.getPayload().getEmail());
}

private Optional<GoogleIdToken> getAuthorizedGoogleIdentity(String token) {
try {
final @Nullable GoogleIdToken googleIdToken = googleIdTokenVerifier.verify(token);

if (googleIdToken != null
&& googleIdToken.getPayload().getHostedDomain().equals(requiredHostedDomain)
&& googleIdToken.getPayload().getEmailVerified()
&& configAuthUsers.contains(googleIdToken.getPayload().getEmail())) {

return Optional.of(googleIdToken);
}

return Optional.empty();

} catch (final Exception ignored) {
return Optional.empty();
}
}

@VisibleForTesting
public static boolean isInBucket(MessageDigest digest, UUID uid, byte[] hashKey, int configPercentage,
Set<UUID> uuidsInBucket) {
Expand Down
Loading

0 comments on commit e084a9f

Please sign in to comment.