Return report spam token from spam check instead of separate call
This commit is contained in:
parent
237d0fd4e2
commit
e9b3e15556
|
@ -208,7 +208,6 @@ import org.whispersystems.textsecuregcm.securevaluerecovery.SecureValueRecovery2
|
||||||
import org.whispersystems.textsecuregcm.spam.ChallengeConstraintChecker;
|
import org.whispersystems.textsecuregcm.spam.ChallengeConstraintChecker;
|
||||||
import org.whispersystems.textsecuregcm.spam.RegistrationFraudChecker;
|
import org.whispersystems.textsecuregcm.spam.RegistrationFraudChecker;
|
||||||
import org.whispersystems.textsecuregcm.spam.RegistrationRecoveryChecker;
|
import org.whispersystems.textsecuregcm.spam.RegistrationRecoveryChecker;
|
||||||
import org.whispersystems.textsecuregcm.spam.ReportSpamTokenProvider;
|
|
||||||
import org.whispersystems.textsecuregcm.spam.SpamChecker;
|
import org.whispersystems.textsecuregcm.spam.SpamChecker;
|
||||||
import org.whispersystems.textsecuregcm.spam.SpamFilter;
|
import org.whispersystems.textsecuregcm.spam.SpamFilter;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountLockManager;
|
import org.whispersystems.textsecuregcm.storage.AccountLockManager;
|
||||||
|
@ -1049,12 +1048,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
if (spamFilter.isEmpty()) {
|
if (spamFilter.isEmpty()) {
|
||||||
log.warn("No spam filters installed");
|
log.warn("No spam filters installed");
|
||||||
}
|
}
|
||||||
final ReportSpamTokenProvider reportSpamTokenProvider = spamFilter
|
|
||||||
.map(SpamFilter::getReportSpamTokenProvider)
|
|
||||||
.orElseGet(() -> {
|
|
||||||
log.warn("No spam-reporting token providers found; using default (no-op) provider as a default");
|
|
||||||
return ReportSpamTokenProvider.noop();
|
|
||||||
});
|
|
||||||
final SpamChecker spamChecker = spamFilter
|
final SpamChecker spamChecker = spamFilter
|
||||||
.map(SpamFilter::getSpamChecker)
|
.map(SpamFilter::getSpamChecker)
|
||||||
.orElseGet(() -> {
|
.orElseGet(() -> {
|
||||||
|
@ -1123,7 +1116,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
|
||||||
new KeyTransparencyController(keyTransparencyServiceClient),
|
new KeyTransparencyController(keyTransparencyServiceClient),
|
||||||
new MessageController(rateLimiters, messageByteLimitCardinalityEstimator, messageSender, receiptSender,
|
new MessageController(rateLimiters, messageByteLimitCardinalityEstimator, messageSender, receiptSender,
|
||||||
accountsManager, messagesManager, pushNotificationManager, pushNotificationScheduler, reportMessageManager,
|
accountsManager, messagesManager, pushNotificationManager, pushNotificationScheduler, reportMessageManager,
|
||||||
multiRecipientMessageExecutor, messageDeliveryScheduler, reportSpamTokenProvider, clientReleaseManager,
|
multiRecipientMessageExecutor, messageDeliveryScheduler, clientReleaseManager,
|
||||||
dynamicConfigurationManager, zkSecretParams, spamChecker, messageMetrics, messageDeliveryLoopMonitor,
|
dynamicConfigurationManager, zkSecretParams, spamChecker, messageMetrics, messageDeliveryLoopMonitor,
|
||||||
Clock.systemUTC()),
|
Clock.systemUTC()),
|
||||||
new PaymentsController(currencyManager, paymentsCredentialsGenerator),
|
new PaymentsController(currencyManager, paymentsCredentialsGenerator),
|
||||||
|
|
|
@ -116,7 +116,6 @@ import org.whispersystems.textsecuregcm.push.MessageSender;
|
||||||
import org.whispersystems.textsecuregcm.push.PushNotificationManager;
|
import org.whispersystems.textsecuregcm.push.PushNotificationManager;
|
||||||
import org.whispersystems.textsecuregcm.push.PushNotificationScheduler;
|
import org.whispersystems.textsecuregcm.push.PushNotificationScheduler;
|
||||||
import org.whispersystems.textsecuregcm.push.ReceiptSender;
|
import org.whispersystems.textsecuregcm.push.ReceiptSender;
|
||||||
import org.whispersystems.textsecuregcm.spam.ReportSpamTokenProvider;
|
|
||||||
import org.whispersystems.textsecuregcm.spam.SpamChecker;
|
import org.whispersystems.textsecuregcm.spam.SpamChecker;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
|
@ -163,7 +162,6 @@ public class MessageController {
|
||||||
private final ReportMessageManager reportMessageManager;
|
private final ReportMessageManager reportMessageManager;
|
||||||
private final ExecutorService multiRecipientMessageExecutor;
|
private final ExecutorService multiRecipientMessageExecutor;
|
||||||
private final Scheduler messageDeliveryScheduler;
|
private final Scheduler messageDeliveryScheduler;
|
||||||
private final ReportSpamTokenProvider reportSpamTokenProvider;
|
|
||||||
private final ClientReleaseManager clientReleaseManager;
|
private final ClientReleaseManager clientReleaseManager;
|
||||||
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
|
private final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager;
|
||||||
private final ServerSecretParams serverSecretParams;
|
private final ServerSecretParams serverSecretParams;
|
||||||
|
@ -226,7 +224,6 @@ public class MessageController {
|
||||||
ReportMessageManager reportMessageManager,
|
ReportMessageManager reportMessageManager,
|
||||||
@Nonnull ExecutorService multiRecipientMessageExecutor,
|
@Nonnull ExecutorService multiRecipientMessageExecutor,
|
||||||
Scheduler messageDeliveryScheduler,
|
Scheduler messageDeliveryScheduler,
|
||||||
@Nonnull ReportSpamTokenProvider reportSpamTokenProvider,
|
|
||||||
final ClientReleaseManager clientReleaseManager,
|
final ClientReleaseManager clientReleaseManager,
|
||||||
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager,
|
final DynamicConfigurationManager<DynamicConfiguration> dynamicConfigurationManager,
|
||||||
final ServerSecretParams serverSecretParams,
|
final ServerSecretParams serverSecretParams,
|
||||||
|
@ -245,7 +242,6 @@ public class MessageController {
|
||||||
this.reportMessageManager = reportMessageManager;
|
this.reportMessageManager = reportMessageManager;
|
||||||
this.multiRecipientMessageExecutor = Objects.requireNonNull(multiRecipientMessageExecutor);
|
this.multiRecipientMessageExecutor = Objects.requireNonNull(multiRecipientMessageExecutor);
|
||||||
this.messageDeliveryScheduler = messageDeliveryScheduler;
|
this.messageDeliveryScheduler = messageDeliveryScheduler;
|
||||||
this.reportSpamTokenProvider = reportSpamTokenProvider;
|
|
||||||
this.clientReleaseManager = clientReleaseManager;
|
this.clientReleaseManager = clientReleaseManager;
|
||||||
this.dynamicConfigurationManager = dynamicConfigurationManager;
|
this.dynamicConfigurationManager = dynamicConfigurationManager;
|
||||||
this.serverSecretParams = serverSecretParams;
|
this.serverSecretParams = serverSecretParams;
|
||||||
|
@ -304,7 +300,7 @@ public class MessageController {
|
||||||
final Sample sample = Timer.start();
|
final Sample sample = Timer.start();
|
||||||
try {
|
try {
|
||||||
if (source.isEmpty() && accessKey.isEmpty() && groupSendToken == null && !isStory) {
|
if (source.isEmpty() && accessKey.isEmpty() && groupSendToken == null && !isStory) {
|
||||||
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
|
throw new WebApplicationException(Status.UNAUTHORIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupSendToken != null) {
|
if (groupSendToken != null) {
|
||||||
|
@ -333,17 +329,14 @@ public class MessageController {
|
||||||
destination = source.map(AuthenticatedDevice::getAccount);
|
destination = source.map(AuthenticatedDevice::getAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Optional<Response> spamCheck = spamChecker.checkForSpam(
|
final SpamChecker.SpamCheckResult spamCheck = spamChecker.checkForSpam(
|
||||||
context, source.map(AuthenticatedDevice::getAccount), destination);
|
context, source, destination);
|
||||||
if (spamCheck.isPresent()) {
|
final Optional<byte[]> reportSpamToken;
|
||||||
return spamCheck.get();
|
switch (spamCheck) {
|
||||||
|
case final SpamChecker.Spam spam: return spam.response();
|
||||||
|
case final SpamChecker.NotSpam notSpam: reportSpamToken = notSpam.token();
|
||||||
}
|
}
|
||||||
|
|
||||||
final Optional<byte[]> spamReportToken = switch (senderType) {
|
|
||||||
case SENDER_TYPE_IDENTIFIED -> reportSpamTokenProvider.makeReportSpamToken(context, source.get(), destination);
|
|
||||||
default -> Optional.empty();
|
|
||||||
};
|
|
||||||
|
|
||||||
int totalContentLength = 0;
|
int totalContentLength = 0;
|
||||||
|
|
||||||
for (final IncomingMessage message : messages.messages()) {
|
for (final IncomingMessage message : messages.messages()) {
|
||||||
|
@ -453,7 +446,7 @@ public class MessageController {
|
||||||
messages.urgent(),
|
messages.urgent(),
|
||||||
incomingMessage,
|
incomingMessage,
|
||||||
userAgent,
|
userAgent,
|
||||||
spamReportToken);
|
reportSpamToken);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -555,9 +548,9 @@ public class MessageController {
|
||||||
|
|
||||||
@Context ContainerRequestContext context) throws RateLimitExceededException {
|
@Context ContainerRequestContext context) throws RateLimitExceededException {
|
||||||
|
|
||||||
final Optional<Response> spamCheck = spamChecker.checkForSpam(context, Optional.empty(), Optional.empty());
|
final SpamChecker.SpamCheckResult spamCheck = spamChecker.checkForSpam(context, Optional.empty(), Optional.empty());
|
||||||
if (spamCheck.isPresent()) {
|
if (spamCheck instanceof final SpamChecker.Spam spam) {
|
||||||
return spamCheck.get();
|
return spam.response();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (groupSendToken == null && accessKeys == null && !isStory) {
|
if (groupSendToken == null && accessKeys == null && !isStory) {
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
package org.whispersystems.textsecuregcm.spam;
|
|
||||||
|
|
||||||
import org.whispersystems.textsecuregcm.auth.AccountAndAuthenticatedDeviceHolder;
|
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
|
||||||
import javax.ws.rs.container.ContainerRequestContext;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates ReportSpamTokens to be used for spam reports.
|
|
||||||
*/
|
|
||||||
public interface ReportSpamTokenProvider {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate a new ReportSpamToken
|
|
||||||
*
|
|
||||||
* @param context the message request context
|
|
||||||
* @param sender the account that sent the unsealed sender message
|
|
||||||
* @param maybeDestination the intended recepient of the message if available
|
|
||||||
* @return either a generated token or nothing
|
|
||||||
*/
|
|
||||||
Optional<byte[]> makeReportSpamToken(ContainerRequestContext context, final AccountAndAuthenticatedDeviceHolder sender,
|
|
||||||
final Optional<Account> maybeDestination);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provider which generates nothing
|
|
||||||
*
|
|
||||||
* @return the provider
|
|
||||||
*/
|
|
||||||
static ReportSpamTokenProvider noop() {
|
|
||||||
return (ignoredContext, ignoredSender, ignoredDest) -> Optional.empty();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,6 +4,7 @@
|
||||||
*/
|
*/
|
||||||
package org.whispersystems.textsecuregcm.spam;
|
package org.whispersystems.textsecuregcm.spam;
|
||||||
|
|
||||||
|
import org.whispersystems.textsecuregcm.auth.AccountAndAuthenticatedDeviceHolder;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import javax.ws.rs.container.ContainerRequestContext;
|
import javax.ws.rs.container.ContainerRequestContext;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
|
@ -11,6 +12,25 @@ import java.util.Optional;
|
||||||
|
|
||||||
public interface SpamChecker {
|
public interface SpamChecker {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A result from the spam checker that is one of:
|
||||||
|
* <ul>
|
||||||
|
* <li>
|
||||||
|
* Message is determined to be spam, and a response is returned
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* Message is not spam, and an optional spam token is returned
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
sealed interface SpamCheckResult {}
|
||||||
|
|
||||||
|
record Spam(Response response) implements SpamCheckResult {}
|
||||||
|
|
||||||
|
record NotSpam(Optional<byte[]> token) implements SpamCheckResult {
|
||||||
|
public static final NotSpam EMPTY_TOKEN = new NotSpam(Optional.empty());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if a message may be spam
|
* Determine if a message may be spam
|
||||||
*
|
*
|
||||||
|
@ -18,14 +38,14 @@ public interface SpamChecker {
|
||||||
* @param maybeSource The sender of the message, could be empty if this as message sent with sealed sender
|
* @param maybeSource The sender of the message, could be empty if this as message sent with sealed sender
|
||||||
* @param maybeDestination The destination of the message, could be empty if the destination does not exist or could
|
* @param maybeDestination The destination of the message, could be empty if the destination does not exist or could
|
||||||
* not be retrieved
|
* not be retrieved
|
||||||
* @return A response to return if the request is determined to be spam, otherwise empty if the message should be sent
|
* @return A {@link SpamCheckResult}
|
||||||
*/
|
*/
|
||||||
Optional<Response> checkForSpam(
|
SpamCheckResult checkForSpam(
|
||||||
final ContainerRequestContext requestContext,
|
final ContainerRequestContext requestContext,
|
||||||
final Optional<Account> maybeSource,
|
final Optional<? extends AccountAndAuthenticatedDeviceHolder> maybeSource,
|
||||||
final Optional<Account> maybeDestination);
|
final Optional<Account> maybeDestination);
|
||||||
|
|
||||||
static SpamChecker noop() {
|
static SpamChecker noop() {
|
||||||
return (ignoredContext, ignoredSource, ignoredDestination) -> Optional.empty();
|
return (ignoredContext, ignoredSource, ignoredDestination) -> NotSpam.EMPTY_TOKEN;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,13 +33,6 @@ public interface SpamFilter extends Managed {
|
||||||
*/
|
*/
|
||||||
void configure(String environmentName, Validator validator) throws IOException, ConfigurationValidationException;
|
void configure(String environmentName, Validator validator) throws IOException, ConfigurationValidationException;
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a spam report token provider. This will generate tokens used by the spam reporting system.
|
|
||||||
*
|
|
||||||
* @return the configured spam report token provider.
|
|
||||||
*/
|
|
||||||
ReportSpamTokenProvider getReportSpamTokenProvider();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a reported message listener controlled by the spam filter. Listeners will be registered with the
|
* Return a reported message listener controlled by the spam filter. Listeners will be registered with the
|
||||||
* {@link org.whispersystems.textsecuregcm.storage.ReportMessageManager}.
|
* {@link org.whispersystems.textsecuregcm.storage.ReportMessageManager}.
|
||||||
|
|
|
@ -114,7 +114,6 @@ import org.whispersystems.textsecuregcm.push.MessageSender;
|
||||||
import org.whispersystems.textsecuregcm.push.PushNotificationManager;
|
import org.whispersystems.textsecuregcm.push.PushNotificationManager;
|
||||||
import org.whispersystems.textsecuregcm.push.PushNotificationScheduler;
|
import org.whispersystems.textsecuregcm.push.PushNotificationScheduler;
|
||||||
import org.whispersystems.textsecuregcm.push.ReceiptSender;
|
import org.whispersystems.textsecuregcm.push.ReceiptSender;
|
||||||
import org.whispersystems.textsecuregcm.spam.ReportSpamTokenProvider;
|
|
||||||
import org.whispersystems.textsecuregcm.spam.SpamChecker;
|
import org.whispersystems.textsecuregcm.spam.SpamChecker;
|
||||||
import org.whispersystems.textsecuregcm.storage.Account;
|
import org.whispersystems.textsecuregcm.storage.Account;
|
||||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||||
|
@ -207,7 +206,7 @@ class MessageControllerTest {
|
||||||
.addResource(
|
.addResource(
|
||||||
new MessageController(rateLimiters, cardinalityEstimator, messageSender, receiptSender, accountsManager,
|
new MessageController(rateLimiters, cardinalityEstimator, messageSender, receiptSender, accountsManager,
|
||||||
messagesManager, pushNotificationManager, pushNotificationScheduler, reportMessageManager, multiRecipientMessageExecutor,
|
messagesManager, pushNotificationManager, pushNotificationScheduler, reportMessageManager, multiRecipientMessageExecutor,
|
||||||
messageDeliveryScheduler, ReportSpamTokenProvider.noop(), mock(ClientReleaseManager.class), dynamicConfigurationManager,
|
messageDeliveryScheduler, mock(ClientReleaseManager.class), dynamicConfigurationManager,
|
||||||
serverSecretParams, SpamChecker.noop(), new MessageMetrics(), mock(MessageDeliveryLoopMonitor.class),
|
serverSecretParams, SpamChecker.noop(), new MessageMetrics(), mock(MessageDeliveryLoopMonitor.class),
|
||||||
clock))
|
clock))
|
||||||
.build();
|
.build();
|
||||||
|
|
Loading…
Reference in New Issue