Remove PUT/DELETE methods from RemoteConfigController

This commit is contained in:
Chris Eager 2023-11-29 16:18:57 -06:00 committed by Jon Chambers
parent 664f9f36e1
commit e084a9f2b6
6 changed files with 15 additions and 370 deletions

View File

@ -337,17 +337,6 @@ appConfig:
configuration: example configuration: example
remoteConfig: 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 globalConfig: # keys and values that are given to clients on GET /v1/config
EXAMPLE_KEY: VALUE EXAMPLE_KEY: VALUE

View File

@ -7,9 +7,6 @@ package org.whispersystems.textsecuregcm;
import static com.codahale.metrics.MetricRegistry.name; import static com.codahale.metrics.MetricRegistry.name;
import static java.util.Objects.requireNonNull; 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.auth.oauth2.GoogleCredentials;
import com.google.cloud.logging.LoggingOptions; import com.google.cloud.logging.LoggingOptions;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
@ -808,14 +805,20 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
new AccountControllerV2(accountsManager, changeNumberManager, phoneVerificationTokenManager, new AccountControllerV2(accountsManager, changeNumberManager, phoneVerificationTokenManager,
registrationLockVerificationManager, rateLimiters), registrationLockVerificationManager, rateLimiters),
new ArtController(rateLimiters, artCredentialsGenerator), 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 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 ArchiveController(backupAuthManager, backupManager),
new CallLinkController(rateLimiters, callingGenericZkSecretParams), 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 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), rateLimitersCluster, config.getMaxDevices(), clock),
new DirectoryV2Controller(directoryV2CredentialsGenerator), new DirectoryV2Controller(directoryV2CredentialsGenerator),
new DonationController(clock, zkReceiptOperations, redeemedReceiptsManager, accountsManager, config.getBadges(), new DonationController(clock, zkReceiptOperations, redeemedReceiptsManager, accountsManager, config.getBadges(),
@ -831,13 +834,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
new ProvisioningController(rateLimiters, provisioningManager), new ProvisioningController(rateLimiters, provisioningManager),
new RegistrationController(accountsManager, phoneVerificationTokenManager, registrationLockVerificationManager, new RegistrationController(accountsManager, phoneVerificationTokenManager, registrationLockVerificationManager,
rateLimiters), rateLimiters),
new RemoteConfigController(remoteConfigsManager, adminEventLogger, new RemoteConfigController(remoteConfigsManager, config.getRemoteConfigConfiguration().globalConfig(), clock),
config.getRemoteConfigConfiguration().authorizedUsers(),
config.getRemoteConfigConfiguration().requiredHostedDomain(),
config.getRemoteConfigConfiguration().audiences(),
new GoogleIdTokenVerifier.Builder(new ApacheHttpTransport(), new GsonFactory()),
config.getRemoteConfigConfiguration().globalConfig(),
clock),
new SecureStorageController(storageCredentialsGenerator), new SecureStorageController(storageCredentialsGenerator),
new SecureValueRecovery2Controller(svr2CredentialsGenerator, accountsManager), new SecureValueRecovery2Controller(svr2CredentialsGenerator, accountsManager),
new SecureValueRecovery3Controller(svr3CredentialsGenerator, accountsManager), new SecureValueRecovery3Controller(svr3CredentialsGenerator, accountsManager),

View File

@ -5,15 +5,9 @@
package org.whispersystems.textsecuregcm.configuration; package org.whispersystems.textsecuregcm.configuration;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
public record RemoteConfigConfiguration(@NotNull Set<String> authorizedUsers, public record RemoteConfigConfiguration(@NotNull Map<String, String> globalConfig) {
@NotNull String requiredHostedDomain,
@NotNull @NotEmpty List<String> audiences,
@NotNull Map<String, String> globalConfig) {
} }

View File

@ -5,8 +5,6 @@
package org.whispersystems.textsecuregcm.controllers; 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 com.google.common.annotations.VisibleForTesting;
import io.dropwizard.auth.Auth; import io.dropwizard.auth.Auth;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
@ -15,35 +13,18 @@ import java.nio.charset.StandardCharsets;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.time.Clock; import java.time.Clock;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; 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.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.PUT;
import javax.ws.rs.Path; import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType; 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.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.entities.UserRemoteConfig; import org.whispersystems.textsecuregcm.entities.UserRemoteConfig;
import org.whispersystems.textsecuregcm.entities.UserRemoteConfigList; import org.whispersystems.textsecuregcm.entities.UserRemoteConfigList;
import org.whispersystems.textsecuregcm.storage.RemoteConfig;
import org.whispersystems.textsecuregcm.storage.RemoteConfigsManager; import org.whispersystems.textsecuregcm.storage.RemoteConfigsManager;
import org.whispersystems.textsecuregcm.util.Conversions; import org.whispersystems.textsecuregcm.util.Conversions;
import org.whispersystems.textsecuregcm.util.Util; import org.whispersystems.textsecuregcm.util.Util;
@ -53,30 +34,18 @@ import org.whispersystems.textsecuregcm.util.Util;
public class RemoteConfigController { public class RemoteConfigController {
private final RemoteConfigsManager remoteConfigsManager; private final RemoteConfigsManager remoteConfigsManager;
private final AdminEventLogger adminEventLogger;
private final Set<String> configAuthUsers;
private final Map<String, String> globalConfig; private final Map<String, String> globalConfig;
private final String requiredHostedDomain;
private final GoogleIdTokenVerifier googleIdTokenVerifier;
private final Clock clock; private final Clock clock;
private static final String GLOBAL_CONFIG_PREFIX = "global."; private static final String GLOBAL_CONFIG_PREFIX = "global.";
public RemoteConfigController(RemoteConfigsManager remoteConfigsManager, AdminEventLogger adminEventLogger, public RemoteConfigController(RemoteConfigsManager remoteConfigsManager,
Set<String> configAuthUsers, String requiredHostedDomain, List<String> audience, Map<String, String> globalConfig,
final GoogleIdTokenVerifier.Builder googleIdTokenVerifierBuilder, Map<String, String> globalConfig,
final Clock clock) { final Clock clock) {
this.remoteConfigsManager = remoteConfigsManager; this.remoteConfigsManager = remoteConfigsManager;
this.adminEventLogger = Objects.requireNonNull(adminEventLogger);
this.configAuthUsers = configAuthUsers;
this.globalConfig = globalConfig; this.globalConfig = globalConfig;
this.requiredHostedDomain = requiredHostedDomain;
this.googleIdTokenVerifier = googleIdTokenVerifierBuilder.setAudience(audience).build();
this.clock = clock; this.clock = clock;
} }
@ -101,68 +70,6 @@ public class RemoteConfigController {
} }
} }
@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 @VisibleForTesting
public static boolean isInBucket(MessageDigest digest, UUID uid, byte[] hashKey, int configPercentage, public static boolean isInBucket(MessageDigest digest, UUID uid, byte[] hashKey, int configPercentage,
Set<UUID> uuidsInBucket) { Set<UUID> uuidsInBucket) {

View File

@ -6,8 +6,6 @@
package org.whispersystems.textsecuregcm.controllers; package org.whispersystems.textsecuregcm.controllers;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset; import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
@ -15,8 +13,6 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;
import com.google.common.collect.ImmutableSet; import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
@ -24,7 +20,6 @@ import io.dropwizard.testing.junit5.ResourceExtension;
import java.security.MessageDigest; import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.time.Instant; import java.time.Instant;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList; import java.util.LinkedList;
@ -32,8 +27,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import org.assertj.core.api.InstanceOfAssertFactory; import org.assertj.core.api.InstanceOfAssertFactory;
@ -42,8 +35,6 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.signal.event.NoOpAdminEventLogger;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.entities.UserRemoteConfig; import org.whispersystems.textsecuregcm.entities.UserRemoteConfig;
@ -59,18 +50,6 @@ class RemoteConfigControllerTest {
private static final RemoteConfigsManager remoteConfigsManager = mock(RemoteConfigsManager.class); private static final RemoteConfigsManager remoteConfigsManager = mock(RemoteConfigsManager.class);
private static final Set<String> remoteConfigsUsers = Set.of("user1@example.com", "user2@example.com");
private static final String requiredHostedDomain = "example.com";
private static final GoogleIdTokenVerifier.Builder googleIdVerificationTokenBuilder = mock(
GoogleIdTokenVerifier.Builder.class);
private static final GoogleIdTokenVerifier googleIdTokenVerifier = mock(GoogleIdTokenVerifier.class);
static {
when(googleIdVerificationTokenBuilder.setAudience(any())).thenReturn(googleIdVerificationTokenBuilder);
when(googleIdVerificationTokenBuilder.build()).thenReturn(googleIdTokenVerifier);
}
private static final long PINNED_EPOCH_SECONDS = 1701287216L; private static final long PINNED_EPOCH_SECONDS = 1701287216L;
private static final TestClock TEST_CLOCK = TestClock.pinned(Instant.ofEpochSecond(PINNED_EPOCH_SECONDS)); private static final TestClock TEST_CLOCK = TestClock.pinned(Instant.ofEpochSecond(PINNED_EPOCH_SECONDS));
@ -81,9 +60,7 @@ class RemoteConfigControllerTest {
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class))) ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addProvider(new DeviceLimitExceededExceptionMapper()) .addProvider(new DeviceLimitExceededExceptionMapper())
.addResource(new RemoteConfigController(remoteConfigsManager, new NoOpAdminEventLogger(), remoteConfigsUsers, .addResource(new RemoteConfigController(remoteConfigsManager, Map.of("maxGroupSize", "42"), TEST_CLOCK))
requiredHostedDomain, Collections.singletonList("aud.example.com"),
googleIdVerificationTokenBuilder, Map.of("maxGroupSize", "42"), TEST_CLOCK))
.build(); .build();
@ -103,23 +80,6 @@ class RemoteConfigControllerTest {
add(new RemoteConfig("unlinked.config", 50, Set.of(), null, null, null)); add(new RemoteConfig("unlinked.config", 50, Set.of(), null, null, null));
}}); }});
final Map<String, GoogleIdToken> googleIdTokens = new HashMap<>();
for (int i = 1; i <= 3; i++) {
final String user = "user" + i;
final GoogleIdToken googleIdToken = mock(GoogleIdToken.class);
final GoogleIdToken.Payload payload = mock(GoogleIdToken.Payload.class);
when(googleIdToken.getPayload()).thenReturn(payload);
when(payload.getEmail()).thenReturn(user + "@" + requiredHostedDomain);
when(payload.getEmailVerified()).thenReturn(true);
when(payload.getHostedDomain()).thenReturn(requiredHostedDomain);
googleIdTokens.put(user + ".valid", googleIdToken);
}
when(googleIdTokenVerifier.verify(anyString()))
.thenAnswer(answer -> googleIdTokens.get(answer.getArgument(0, String.class)));
} }
@AfterEach @AfterEach
@ -244,188 +204,6 @@ class RemoteConfigControllerTest {
verifyNoMoreInteractions(remoteConfigsManager); verifyNoMoreInteractions(remoteConfigsManager);
} }
@Test
void testSetConfig() {
Response response = resources.getJerseyTest()
.target("/v1/config")
.request()
.header("Config-Token", "user1.valid")
.put(Entity.entity(new RemoteConfig("android.stickers", 88, Set.of(), "FALSE", "TRUE", null),
MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(204);
ArgumentCaptor<RemoteConfig> captor = ArgumentCaptor.forClass(RemoteConfig.class);
verify(remoteConfigsManager, times(1)).set(captor.capture());
assertThat(captor.getValue().getName()).isEqualTo("android.stickers");
assertThat(captor.getValue().getPercentage()).isEqualTo(88);
assertThat(captor.getValue().getUuids()).isEmpty();
}
@Test
void testSetConfigValued() {
Response response = resources.getJerseyTest()
.target("/v1/config")
.request()
.header("Config-Token", "user1.valid")
.put(Entity.entity(new RemoteConfig("value.sometimes", 50, Set.of(), "a", "b", null),
MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(204);
ArgumentCaptor<RemoteConfig> captor = ArgumentCaptor.forClass(RemoteConfig.class);
verify(remoteConfigsManager, times(1)).set(captor.capture());
assertThat(captor.getValue().getName()).isEqualTo("value.sometimes");
assertThat(captor.getValue().getPercentage()).isEqualTo(50);
assertThat(captor.getValue().getUuids()).isEmpty();
}
@Test
void testSetConfigWithHashKey() {
final String configToken = "user1.valid";
Response response1 = resources.getJerseyTest()
.target("/v1/config")
.request()
.header("Config-Token", configToken)
.put(Entity.entity(new RemoteConfig("linked.config.0", 50, Set.of(), "FALSE", "TRUE", null),
MediaType.APPLICATION_JSON_TYPE));
assertThat(response1.getStatus()).isEqualTo(204);
Response response2 = resources.getJerseyTest()
.target("/v1/config")
.request()
.header("Config-Token", configToken)
.put(Entity.entity(new RemoteConfig("linked.config.1", 50, Set.of(), "FALSE", "TRUE", "linked.config.0"), MediaType.APPLICATION_JSON_TYPE));
assertThat(response2.getStatus()).isEqualTo(204);
ArgumentCaptor<RemoteConfig> captor = ArgumentCaptor.forClass(RemoteConfig.class);
verify(remoteConfigsManager, times(2)).set(captor.capture());
assertThat(captor.getAllValues()).hasSize(2);
final RemoteConfig capture1 = captor.getAllValues().get(0);
assertThat(capture1).isNotNull();
assertThat(capture1.getName()).isEqualTo("linked.config.0");
assertThat(capture1.getPercentage()).isEqualTo(50);
assertThat(capture1.getUuids()).isEmpty();
assertThat(capture1.getHashKey()).isNull();
final RemoteConfig capture2 = captor.getAllValues().get(1);
assertThat(capture2).isNotNull();
assertThat(capture2.getName()).isEqualTo("linked.config.1");
assertThat(capture2.getPercentage()).isEqualTo(50);
assertThat(capture2.getUuids()).isEmpty();
assertThat(capture2.getHashKey()).isEqualTo("linked.config.0");
}
@Test
void testSetConfigUnauthorized() {
Response response = resources.getJerseyTest()
.target("/v1/config")
.request()
.header("Config-Token", "user3.valid")
.put(Entity.entity(new RemoteConfig("android.stickers", 88, Set.of(), "FALSE", "TRUE", null),
MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(401);
verifyNoMoreInteractions(remoteConfigsManager);
}
@Test
void testSetConfigMissingUnauthorized() {
Response response = resources.getJerseyTest()
.target("/v1/config")
.request()
.put(Entity.entity(new RemoteConfig("android.stickers", 88, Set.of(), "FALSE", "TRUE", null),
MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(401);
verifyNoMoreInteractions(remoteConfigsManager);
}
@Test
void testSetConfigBadName() {
Response response = resources.getJerseyTest()
.target("/v1/config")
.request()
.header("Config-Token", "user1.valid")
.put(Entity.entity(new RemoteConfig("android-stickers", 88, Set.of(), "FALSE", "TRUE", null),
MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(422);
verifyNoMoreInteractions(remoteConfigsManager);
}
@Test
void testSetConfigEmptyName() {
Response response = resources.getJerseyTest()
.target("/v1/config")
.request()
.header("Config-Token", "user1.valid")
.put(Entity.entity(new RemoteConfig("", 88, Set.of(), "FALSE", "TRUE", null), MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(422);
verifyNoMoreInteractions(remoteConfigsManager);
}
@Test
void testSetGlobalConfig() {
Response response = resources.getJerseyTest()
.target("/v1/config")
.request()
.header("Config-Token", "user1.valid")
.put(Entity.entity(new RemoteConfig("global.maxGroupSize", 88, Set.of(), "FALSE", "TRUE", null),
MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(403);
verifyNoMoreInteractions(remoteConfigsManager);
}
@Test
void testDelete() {
Response response = resources.getJerseyTest()
.target("/v1/config/android.stickers")
.request()
.header("Config-Token", "user1.valid")
.delete();
assertThat(response.getStatus()).isEqualTo(204);
verify(remoteConfigsManager, times(1)).delete("android.stickers");
verifyNoMoreInteractions(remoteConfigsManager);
}
@Test
void testDeleteUnauthorized() {
Response response = resources.getJerseyTest()
.target("/v1/config/android.stickers")
.request()
.header("Config-Token", "baz")
.delete();
assertThat(response.getStatus()).isEqualTo(401);
verifyNoMoreInteractions(remoteConfigsManager);
}
@Test
void testDeleteGlobalConfig() {
Response response = resources.getJerseyTest()
.target("/v1/config/global.maxGroupSize")
.request()
.header("Config-Token", "user1.valid")
.delete();
assertThat(response.getStatus()).isEqualTo(403);
verifyNoMoreInteractions(remoteConfigsManager);
}
@Test @Test
void testMath() throws NoSuchAlgorithmException { void testMath() throws NoSuchAlgorithmException {
List<RemoteConfig> remoteConfigList = remoteConfigsManager.getAll(); List<RemoteConfig> remoteConfigList = remoteConfigsManager.getAll();

View File

@ -31,24 +31,4 @@ class RemoteConfigsManagerTest {
// A memoized supplier should prevent multiple calls to the underlying data source // A memoized supplier should prevent multiple calls to the underlying data source
verify(remoteConfigs, times(1)).getAll(); verify(remoteConfigs, times(1)).getAll();
} }
@Test
void testSet() {
final RemoteConfig remoteConfig = mock(RemoteConfig.class);
remoteConfigsManager.set(remoteConfig);
remoteConfigsManager.set(remoteConfig);
verify(remoteConfigs, times(2)).set(remoteConfig);
}
@Test
void testDelete() {
final String name = "name";
remoteConfigsManager.delete(name);
remoteConfigsManager.delete(name);
verify(remoteConfigs, times(2)).delete(name);
}
} }