mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-20 12:08:05 +01:00
Explicitly call spam-filter for verification session updates
Pass in the same information to the spam-filter, but just use explicit method calls rather than jersey request filters.
This commit is contained in:
committed by
ravi-signal
parent
4f40c128bf
commit
69330f47fd
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.spam;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Indicates that a parameter should be parsed from a {@link org.glassfish.jersey.server.ContainerRequest}
|
||||
*/
|
||||
@Target({ElementType.PARAMETER, ElementType.FIELD})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface Extract {
|
||||
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2021 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.spam;
|
||||
|
||||
import javax.ws.rs.NameBinding;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* A name-binding annotation that associates {@link SpamFilter}s with resource methods.
|
||||
*/
|
||||
@NameBinding
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.TYPE, ElementType.METHOD})
|
||||
public @interface FilterSpam {
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.spam;
|
||||
|
||||
import java.util.Optional;
|
||||
import javax.ws.rs.container.ContainerRequestContext;
|
||||
import org.whispersystems.textsecuregcm.entities.UpdateVerificationSessionRequest;
|
||||
import org.whispersystems.textsecuregcm.registration.VerificationSession;
|
||||
|
||||
public interface RegistrationFraudChecker {
|
||||
|
||||
record VerificationCheck(Optional<VerificationSession> updatedSession, Optional<Float> scoreThreshold) {}
|
||||
|
||||
/**
|
||||
* Determine if a registration attempt is suspicious
|
||||
*
|
||||
* @param requestContext The request context for an update verification session attempt
|
||||
* @param verificationSession The target verification session
|
||||
* @param e164 The target phone number
|
||||
* @param request The information to add to the verification session
|
||||
* @return A SessionUpdate including updates to the verification session that should be persisted to the caller along
|
||||
* with other constraints to enforce when evaluating the UpdateVerificationSessionRequest.
|
||||
*/
|
||||
VerificationCheck checkVerificationAttempt(
|
||||
final ContainerRequestContext requestContext,
|
||||
final VerificationSession verificationSession,
|
||||
final String e164,
|
||||
final UpdateVerificationSessionRequest request);
|
||||
|
||||
static RegistrationFraudChecker noop() {
|
||||
return (ignoredContext, ignoredSession, ignoredE164, ignoredRequest) -> new VerificationCheck(
|
||||
Optional.empty(),
|
||||
Optional.empty());
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.spam;
|
||||
|
||||
import org.glassfish.jersey.server.ContainerRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A ScoreThreshold may be provided by an upstream request filter. If request contains a property for
|
||||
* {@link #PROPERTY_NAME} it can be forwarded to a downstream filter to indicate it can use a more or less strict
|
||||
* score threshold when evaluating whether a request should be allowed to continue.
|
||||
*/
|
||||
public class ScoreThreshold {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(ScoreThreshold.class);
|
||||
|
||||
public static final String PROPERTY_NAME = "scoreThreshold";
|
||||
|
||||
/**
|
||||
* A score threshold in the range [0, 1.0]
|
||||
*/
|
||||
private final Optional<Float> scoreThreshold;
|
||||
|
||||
/**
|
||||
* Extract an optional score threshold parameter provided by an upstream request filter
|
||||
*/
|
||||
public ScoreThreshold(final ContainerRequest containerRequest) {
|
||||
this.scoreThreshold = Optional
|
||||
.ofNullable(containerRequest.getProperty(PROPERTY_NAME))
|
||||
.flatMap(obj -> {
|
||||
if (obj instanceof Float f) {
|
||||
return Optional.of(f);
|
||||
}
|
||||
logger.warn("invalid format for filter provided score threshold {}", obj);
|
||||
return Optional.empty();
|
||||
});
|
||||
}
|
||||
|
||||
public Optional<Float> getScoreThreshold() {
|
||||
return this.scoreThreshold;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
package org.whispersystems.textsecuregcm.spam;
|
||||
|
||||
import java.util.function.Function;
|
||||
import javax.inject.Singleton;
|
||||
import javax.ws.rs.core.Feature;
|
||||
import javax.ws.rs.core.FeatureContext;
|
||||
import org.glassfish.jersey.internal.inject.AbstractBinder;
|
||||
import org.glassfish.jersey.server.ContainerRequest;
|
||||
import org.glassfish.jersey.server.model.Parameter;
|
||||
import org.glassfish.jersey.server.spi.internal.ValueParamProvider;
|
||||
|
||||
/**
|
||||
* Parses a {@link ScoreThreshold} out of a {@link ContainerRequest} to provide to jersey resources.
|
||||
* <p>
|
||||
* A request filter may enrich a ContainerRequest with a scoreThreshold by providing a float property with the name
|
||||
* {@link ScoreThreshold#PROPERTY_NAME}. This indicates the desired scoreThreshold to use when evaluating whether a
|
||||
* request should proceed.
|
||||
* <p>
|
||||
* A resource can consume a ScoreThreshold with by annotating a ScoreThreshold parameter with {@link Extract}
|
||||
*/
|
||||
public class ScoreThresholdProvider implements ValueParamProvider {
|
||||
|
||||
/**
|
||||
* Configures the ScoreThresholdProvider
|
||||
*/
|
||||
public static class ScoreThresholdFeature implements Feature {
|
||||
|
||||
@Override
|
||||
public boolean configure(FeatureContext context) {
|
||||
context.register(new AbstractBinder() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(ScoreThresholdProvider.class)
|
||||
.to(ValueParamProvider.class)
|
||||
.in(Singleton.class);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<ContainerRequest, ?> getValueProvider(final Parameter parameter) {
|
||||
if (parameter.getRawType().equals(ScoreThreshold.class)
|
||||
&& parameter.isAnnotationPresent(Extract.class)) {
|
||||
return ScoreThreshold::new;
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PriorityType getPriority() {
|
||||
return Priority.HIGH;
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.spam;
|
||||
|
||||
import org.glassfish.jersey.server.ContainerRequest;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* A SenderOverride may be provided by an upstream request filter. If request contains a property for
|
||||
* {@link #SMS_SENDER_OVERRIDE_PROPERTY_NAME} or {@link #VOICE_SENDER_OVERRIDE_PROPERTY_NAME} it can be
|
||||
* forwarded to a downstream filter to indicate a specific sender should be used when sending verification codes.
|
||||
*/
|
||||
public class SenderOverride {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(SenderOverride.class);
|
||||
public static final String SMS_SENDER_OVERRIDE_PROPERTY_NAME = "smsSenderOverride";
|
||||
public static final String VOICE_SENDER_OVERRIDE_PROPERTY_NAME = "voiceSenderOverride";
|
||||
|
||||
/**
|
||||
* The name of the sender to use to deliver a verification code via SMS
|
||||
*/
|
||||
private final Optional<String> smsSenderOverride;
|
||||
|
||||
/**
|
||||
* The name of the sender to use to deliver a verification code via voice
|
||||
*/
|
||||
private final Optional<String> voiceSenderOverride;
|
||||
|
||||
public SenderOverride(final ContainerRequest containerRequest) {
|
||||
this.smsSenderOverride = parse(String.class, SMS_SENDER_OVERRIDE_PROPERTY_NAME, containerRequest);
|
||||
this.voiceSenderOverride = parse(String.class, VOICE_SENDER_OVERRIDE_PROPERTY_NAME, containerRequest);
|
||||
}
|
||||
|
||||
private static <T> Optional<T> parse(Class<T> type, final String propertyName,
|
||||
final ContainerRequest containerRequest) {
|
||||
return Optional
|
||||
.ofNullable(containerRequest.getProperty(propertyName))
|
||||
.flatMap(obj -> {
|
||||
if (type.isInstance(obj)) {
|
||||
return Optional.of(type.cast(obj));
|
||||
}
|
||||
logger.warn("invalid format for filter provided property {}: {}", propertyName, obj);
|
||||
return Optional.empty();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public Optional<String> getSmsSenderOverride() {
|
||||
return smsSenderOverride;
|
||||
}
|
||||
|
||||
public Optional<String> getVoiceSenderOverride() {
|
||||
return voiceSenderOverride;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.textsecuregcm.spam;
|
||||
|
||||
import java.util.function.Function;
|
||||
import javax.inject.Singleton;
|
||||
import javax.ws.rs.core.Feature;
|
||||
import javax.ws.rs.core.FeatureContext;
|
||||
import org.glassfish.jersey.internal.inject.AbstractBinder;
|
||||
import org.glassfish.jersey.server.ContainerRequest;
|
||||
import org.glassfish.jersey.server.model.Parameter;
|
||||
import org.glassfish.jersey.server.spi.internal.ValueParamProvider;
|
||||
|
||||
/**
|
||||
* Parses a {@link SenderOverride} out of a {@link ContainerRequest} to provide to jersey resources.
|
||||
* <p>
|
||||
* A request filter may enrich a ContainerRequest with senderOverrides by providing a string property names defined in
|
||||
* {@link SenderOverride}. This indicates the desired senderOverride to use when sending verification codes.
|
||||
* <p>
|
||||
* A resource can consume a SenderOverride with by annotating a SenderOverride parameter with {@link Extract}
|
||||
*/
|
||||
public class SenderOverrideProvider implements ValueParamProvider {
|
||||
|
||||
/**
|
||||
* Configures the SenderOverrideProvider
|
||||
*/
|
||||
public static class SenderOverrideFeature implements Feature {
|
||||
|
||||
@Override
|
||||
public boolean configure(FeatureContext context) {
|
||||
context.register(new AbstractBinder() {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(SenderOverrideProvider.class)
|
||||
.to(ValueParamProvider.class)
|
||||
.in(Singleton.class);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Function<ContainerRequest, ?> getValueProvider(final Parameter parameter) {
|
||||
if (parameter.getRawType().equals(SenderOverride.class)
|
||||
&& parameter.isAnnotationPresent(Extract.class)) {
|
||||
return SenderOverride::new;
|
||||
}
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PriorityType getPriority() {
|
||||
return Priority.HIGH;
|
||||
}
|
||||
}
|
||||
@@ -11,17 +11,14 @@ import javax.ws.rs.container.ContainerRequestFilter;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* A spam filter is a {@link ContainerRequestFilter} that filters requests to endpoints to detect and respond to
|
||||
* patterns of spam and fraud.
|
||||
* A spam filter provides various checkers and listeners to detect and respond to patterns of spam and fraud.
|
||||
* <p/>
|
||||
* Spam filters are managed components that are generally loaded dynamically via a {@link java.util.ServiceLoader}.
|
||||
* Their {@link #configure(String)} method will be called prior to be adding to the server's pool of {@link Managed}
|
||||
* objects.
|
||||
* <p/>
|
||||
* Spam filters must be annotated with {@link FilterSpam}, a name binding annotation that restricts the endpoints to
|
||||
* which the filter may apply.
|
||||
*/
|
||||
public interface SpamFilter extends ContainerRequestFilter, Managed {
|
||||
public interface SpamFilter extends Managed {
|
||||
|
||||
/**
|
||||
* Configures this spam filter. This method will be called before the filter is added to the server's pool of managed
|
||||
@@ -65,6 +62,13 @@ public interface SpamFilter extends ContainerRequestFilter, Managed {
|
||||
*/
|
||||
SpamChecker getSpamChecker();
|
||||
|
||||
/**
|
||||
* Return a checker that will be called to check registration attempts
|
||||
*
|
||||
* @return a {@link RegistrationFraudChecker} controlled by the spam filter
|
||||
*/
|
||||
RegistrationFraudChecker getRegistrationFraudChecker();
|
||||
|
||||
/**
|
||||
* Return a checker that will be called to determine what constraints should be applied
|
||||
* when a user requests or solves a challenge (captchas, push challenges, etc).
|
||||
|
||||
Reference in New Issue
Block a user