Create GCP Logging implementation of AdminEventLogger
This commit is contained in:
parent
a4508ec84f
commit
abd0f9630c
|
@ -5,11 +5,36 @@
|
||||||
|
|
||||||
package org.signal.event
|
package org.signal.event
|
||||||
|
|
||||||
interface Logger {
|
import com.google.cloud.logging.LogEntry
|
||||||
|
import com.google.cloud.logging.Logging
|
||||||
|
import com.google.cloud.logging.Payload.JsonPayload
|
||||||
|
import com.google.cloud.logging.Severity
|
||||||
|
import com.google.protobuf.Struct
|
||||||
|
import com.google.protobuf.util.JsonFormat
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
|
interface AdminEventLogger {
|
||||||
fun logEvent(event: Event, labels: Map<String, String>?)
|
fun logEvent(event: Event, labels: Map<String, String>?)
|
||||||
fun logEvent(event: Event) = logEvent(event, null)
|
fun logEvent(event: Event) = logEvent(event, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
class NoOpLogger : Logger {
|
class NoOpAdminEventLogger : AdminEventLogger {
|
||||||
override fun logEvent(event: Event, labels: Map<String, String>?) {}
|
override fun logEvent(event: Event, labels: Map<String, String>?) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class GoogleCloudAdminEventLogger(private val logging: Logging, private val logName: String) : AdminEventLogger {
|
||||||
|
override fun logEvent(event: Event, labels: Map<String, String>?) {
|
||||||
|
val structBuilder = Struct.newBuilder()
|
||||||
|
JsonFormat.parser().merge(Json.encodeToString(event), structBuilder)
|
||||||
|
val struct = structBuilder.build()
|
||||||
|
|
||||||
|
val logEntryBuilder = LogEntry.newBuilder(JsonPayload.of(struct))
|
||||||
|
.setLogName(logName)
|
||||||
|
.setSeverity(Severity.NOTICE);
|
||||||
|
if (labels != null) {
|
||||||
|
logEntryBuilder.setLabels(labels);
|
||||||
|
}
|
||||||
|
logging.write(listOf(logEntryBuilder.build()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
7
pom.xml
7
pom.xml
|
@ -136,6 +136,13 @@
|
||||||
<type>pom</type>
|
<type>pom</type>
|
||||||
<scope>import</scope>
|
<scope>import</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.cloud</groupId>
|
||||||
|
<artifactId>libraries-bom</artifactId>
|
||||||
|
<version>26.1.0</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.eatthepath</groupId>
|
<groupId>com.eatthepath</groupId>
|
||||||
|
|
|
@ -3,6 +3,13 @@
|
||||||
# `unset` values will need to be set to work properly.
|
# `unset` values will need to be set to work properly.
|
||||||
# Most other values are technically valid for a local/demonstration environment, but are probably not production-ready.
|
# Most other values are technically valid for a local/demonstration environment, but are probably not production-ready.
|
||||||
|
|
||||||
|
adminEventLoggingConfiguration:
|
||||||
|
credentials: |
|
||||||
|
Some credentials text
|
||||||
|
blah blah blah
|
||||||
|
projectId: some-project-id
|
||||||
|
logName: some-log-name
|
||||||
|
|
||||||
stripe:
|
stripe:
|
||||||
apiKey: unset
|
apiKey: unset
|
||||||
idempotencyKeyGenerator: abcdefg12345678= # base64 for creating request idempotency hash
|
idempotencyKeyGenerator: abcdefg12345678= # base64 for creating request idempotency hash
|
||||||
|
|
|
@ -14,6 +14,7 @@ import javax.validation.Valid;
|
||||||
import javax.validation.constraints.NotNull;
|
import javax.validation.constraints.NotNull;
|
||||||
import org.whispersystems.textsecuregcm.configuration.AbusiveMessageFilterConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.AbusiveMessageFilterConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.AccountDatabaseCrawlerConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.AccountDatabaseCrawlerConfiguration;
|
||||||
|
import org.whispersystems.textsecuregcm.configuration.AdminEventLoggingConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.ApnConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.ApnConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.AppConfigConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.AppConfigConfiguration;
|
||||||
import org.whispersystems.textsecuregcm.configuration.AwsAttachmentsConfiguration;
|
import org.whispersystems.textsecuregcm.configuration.AwsAttachmentsConfiguration;
|
||||||
|
@ -53,6 +54,11 @@ import org.whispersystems.websocket.configuration.WebSocketConfiguration;
|
||||||
/** @noinspection MismatchedQueryAndUpdateOfCollection, WeakerAccess */
|
/** @noinspection MismatchedQueryAndUpdateOfCollection, WeakerAccess */
|
||||||
public class WhisperServerConfiguration extends Configuration {
|
public class WhisperServerConfiguration extends Configuration {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Valid
|
||||||
|
@JsonProperty
|
||||||
|
private AdminEventLoggingConfiguration adminEventLoggingConfiguration;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@Valid
|
@Valid
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
|
@ -257,6 +263,10 @@ public class WhisperServerConfiguration extends Configuration {
|
||||||
@JsonProperty
|
@JsonProperty
|
||||||
private AbusiveMessageFilterConfiguration abusiveMessageFilter;
|
private AbusiveMessageFilterConfiguration abusiveMessageFilter;
|
||||||
|
|
||||||
|
public AdminEventLoggingConfiguration getAdminEventLoggingConfiguration() {
|
||||||
|
return adminEventLoggingConfiguration;
|
||||||
|
}
|
||||||
|
|
||||||
public StripeConfiguration getStripe() {
|
public StripeConfiguration getStripe() {
|
||||||
return stripe;
|
return stripe;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@ import com.codahale.metrics.SharedMetricRegistries;
|
||||||
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
import com.fasterxml.jackson.annotation.JsonAutoDetect;
|
||||||
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
import com.fasterxml.jackson.annotation.PropertyAccessor;
|
||||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||||
|
import com.google.auth.oauth2.GoogleCredentials;
|
||||||
|
import com.google.cloud.logging.LoggingOptions;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
@ -34,7 +36,9 @@ import io.micrometer.core.instrument.Tags;
|
||||||
import io.micrometer.core.instrument.config.MeterFilter;
|
import io.micrometer.core.instrument.config.MeterFilter;
|
||||||
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
|
import io.micrometer.core.instrument.distribution.DistributionStatisticConfig;
|
||||||
import io.micrometer.datadog.DatadogMeterRegistry;
|
import io.micrometer.datadog.DatadogMeterRegistry;
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
import java.net.http.HttpClient;
|
import java.net.http.HttpClient;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Clock;
|
import java.time.Clock;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -55,7 +59,8 @@ import javax.servlet.FilterRegistration;
|
||||||
import javax.servlet.ServletRegistration;
|
import javax.servlet.ServletRegistration;
|
||||||
import org.eclipse.jetty.servlets.CrossOriginFilter;
|
import org.eclipse.jetty.servlets.CrossOriginFilter;
|
||||||
import org.glassfish.jersey.server.ServerProperties;
|
import org.glassfish.jersey.server.ServerProperties;
|
||||||
import org.signal.event.NoOpLogger;
|
import org.signal.event.AdminEventLogger;
|
||||||
|
import org.signal.event.GoogleCloudAdminEventLogger;
|
||||||
import org.signal.i18n.HeaderControlledResourceBundleLookup;
|
import org.signal.i18n.HeaderControlledResourceBundleLookup;
|
||||||
import org.signal.libsignal.zkgroup.ServerSecretParams;
|
import org.signal.libsignal.zkgroup.ServerSecretParams;
|
||||||
import org.signal.libsignal.zkgroup.auth.ServerZkAuthOperations;
|
import org.signal.libsignal.zkgroup.auth.ServerZkAuthOperations;
|
||||||
|
@ -407,6 +412,13 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
.rejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy())
|
.rejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
final AdminEventLogger adminEventLogger = new GoogleCloudAdminEventLogger(
|
||||||
|
LoggingOptions.newBuilder().setProjectId(config.getAdminEventLoggingConfiguration().projectId())
|
||||||
|
.setCredentials(GoogleCredentials.fromStream(new ByteArrayInputStream(
|
||||||
|
config.getAdminEventLoggingConfiguration().credentials().getBytes(StandardCharsets.UTF_8))))
|
||||||
|
.build().getService(),
|
||||||
|
config.getAdminEventLoggingConfiguration().logName());
|
||||||
|
|
||||||
StripeManager stripeManager = new StripeManager(config.getStripe().getApiKey(), stripeExecutor,
|
StripeManager stripeManager = new StripeManager(config.getStripe().getApiKey(), stripeExecutor,
|
||||||
config.getStripe().getIdempotencyKeyGenerator(), config.getStripe().getBoostDescription());
|
config.getStripe().getIdempotencyKeyGenerator(), config.getStripe().getBoostDescription());
|
||||||
|
|
||||||
|
@ -645,7 +657,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
new PaymentsController(currencyManager, paymentsCredentialsGenerator),
|
new PaymentsController(currencyManager, paymentsCredentialsGenerator),
|
||||||
new ProfileController(clock, rateLimiters, accountsManager, profilesManager, dynamicConfigurationManager, profileBadgeConverter, config.getBadges(), cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner, config.getCdnConfiguration().getBucket(), zkProfileOperations, batchIdentityCheckExecutor),
|
new ProfileController(clock, rateLimiters, accountsManager, profilesManager, dynamicConfigurationManager, profileBadgeConverter, config.getBadges(), cdnS3Client, profileCdnPolicyGenerator, profileCdnPolicySigner, config.getCdnConfiguration().getBucket(), zkProfileOperations, batchIdentityCheckExecutor),
|
||||||
new ProvisioningController(rateLimiters, provisioningManager),
|
new ProvisioningController(rateLimiters, provisioningManager),
|
||||||
new RemoteConfigController(remoteConfigsManager, new NoOpLogger(), config.getRemoteConfigConfiguration().getAuthorizedTokens(), config.getRemoteConfigConfiguration().getGlobalConfig()),
|
new RemoteConfigController(remoteConfigsManager, adminEventLogger, config.getRemoteConfigConfiguration().getAuthorizedTokens(), config.getRemoteConfigConfiguration().getGlobalConfig()),
|
||||||
new SecureBackupController(backupCredentialsGenerator),
|
new SecureBackupController(backupCredentialsGenerator),
|
||||||
new SecureStorageController(storageCredentialsGenerator),
|
new SecureStorageController(storageCredentialsGenerator),
|
||||||
new StickerController(rateLimiters, config.getCdnConfiguration().getAccessKey(),
|
new StickerController(rateLimiters, config.getCdnConfiguration().getAccessKey(),
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 Signal Messenger, LLC
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.whispersystems.textsecuregcm.configuration;
|
||||||
|
|
||||||
|
import javax.validation.constraints.NotEmpty;
|
||||||
|
import javax.validation.constraints.NotNull;
|
||||||
|
|
||||||
|
public record AdminEventLoggingConfiguration(
|
||||||
|
@NotNull @NotEmpty String credentials,
|
||||||
|
@NotNull @NotEmpty String projectId,
|
||||||
|
@NotNull @NotEmpty String logName) {
|
||||||
|
}
|
|
@ -32,7 +32,7 @@ import javax.ws.rs.Produces;
|
||||||
import javax.ws.rs.WebApplicationException;
|
import javax.ws.rs.WebApplicationException;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import org.signal.event.Logger;
|
import org.signal.event.AdminEventLogger;
|
||||||
import org.signal.event.RemoteConfigSetEvent;
|
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;
|
||||||
|
@ -45,15 +45,15 @@ import org.whispersystems.textsecuregcm.util.Conversions;
|
||||||
public class RemoteConfigController {
|
public class RemoteConfigController {
|
||||||
|
|
||||||
private final RemoteConfigsManager remoteConfigsManager;
|
private final RemoteConfigsManager remoteConfigsManager;
|
||||||
private final Logger eventLogger;
|
private final AdminEventLogger adminEventLogger;
|
||||||
private final List<String> configAuthTokens;
|
private final List<String> configAuthTokens;
|
||||||
private final Map<String, String> globalConfig;
|
private final Map<String, String> globalConfig;
|
||||||
|
|
||||||
private static final String GLOBAL_CONFIG_PREFIX = "global.";
|
private static final String GLOBAL_CONFIG_PREFIX = "global.";
|
||||||
|
|
||||||
public RemoteConfigController(RemoteConfigsManager remoteConfigsManager, Logger eventLogger, List<String> configAuthTokens, Map<String, String> globalConfig) {
|
public RemoteConfigController(RemoteConfigsManager remoteConfigsManager, AdminEventLogger adminEventLogger, List<String> configAuthTokens, Map<String, String> globalConfig) {
|
||||||
this.remoteConfigsManager = remoteConfigsManager;
|
this.remoteConfigsManager = remoteConfigsManager;
|
||||||
this.eventLogger = Objects.requireNonNull(eventLogger);
|
this.adminEventLogger = Objects.requireNonNull(adminEventLogger);
|
||||||
this.configAuthTokens = configAuthTokens;
|
this.configAuthTokens = configAuthTokens;
|
||||||
this.globalConfig = globalConfig;
|
this.globalConfig = globalConfig;
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ public class RemoteConfigController {
|
||||||
throw new WebApplicationException(Response.Status.FORBIDDEN);
|
throw new WebApplicationException(Response.Status.FORBIDDEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
eventLogger.logEvent(
|
adminEventLogger.logEvent(
|
||||||
new RemoteConfigSetEvent(
|
new RemoteConfigSetEvent(
|
||||||
configToken,
|
configToken,
|
||||||
config.getName(),
|
config.getName(),
|
||||||
|
|
|
@ -35,7 +35,7 @@ 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.mockito.ArgumentCaptor;
|
||||||
import org.signal.event.NoOpLogger;
|
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.controllers.RemoteConfigController;
|
import org.whispersystems.textsecuregcm.controllers.RemoteConfigController;
|
||||||
|
@ -58,7 +58,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 NoOpLogger(), remoteConfigsAuth, Map.of("maxGroupSize", "42")))
|
.addResource(new RemoteConfigController(remoteConfigsManager, new NoOpAdminEventLogger(), remoteConfigsAuth, Map.of("maxGroupSize", "42")))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue