Explicitly call spam-filter for challenges

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:
Ravi Khadiwala
2024-02-15 18:21:57 -06:00
committed by ravi-signal
parent 30b5ad1515
commit 4f40c128bf
7 changed files with 109 additions and 147 deletions

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.spam;
import org.whispersystems.textsecuregcm.storage.Account;
import javax.ws.rs.container.ContainerRequestContext;
import java.util.Optional;
public interface ChallengeConstraintChecker {
record ChallengeConstraints(boolean pushPermitted, Optional<Float> captchaScoreThreshold) {}
/**
* Retrieve constraints for captcha and push challenges
*
* @param authenticatedAccount The authenticated account attempting to request or solve a challenge
* @return ChallengeConstraints indicating what constraints should be applied to challenges
*/
ChallengeConstraints challengeConstraints(ContainerRequestContext requestContext, Account authenticatedAccount);
static ChallengeConstraintChecker noop() {
return (account, ctx) -> new ChallengeConstraints(true, Optional.empty());
}
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.spam;
import java.util.Optional;
import org.glassfish.jersey.server.ContainerRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A PushChallengeConfig may be provided by an upstream request filter. If request contains a
* property for PROPERTY_NAME it can be forwarded to a downstream filter to indicate whether
* push-token challenges can be used in place of captchas when evaluating whether a request should
* be allowed to continue.
*/
public class PushChallengeConfig {
private static final Logger logger = LoggerFactory.getLogger(PushChallengeConfig.class);
public static final String PROPERTY_NAME = "pushChallengePermitted";
/**
* A score threshold in the range [0, 1.0]
*/
private final boolean pushPermitted;
/**
* Extract an optional score threshold parameter provided by an upstream request filter
*/
public PushChallengeConfig(final ContainerRequest containerRequest) {
this.pushPermitted = Optional
.ofNullable(containerRequest.getProperty(PROPERTY_NAME))
.filter(obj -> obj instanceof Boolean)
.map(obj -> (Boolean) obj)
.orElse(true); // not a typo! true is the default
}
public boolean pushPermitted() {
return pushPermitted;
}
}

View File

@@ -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 PushChallengeConfig} out of a {@link ContainerRequest} to provide to jersey resources.
*
* A request filter may enrich a ContainerRequest with a PushChallengeConfig by providing a float
* property with the name {@link PushChallengeConfig#PROPERTY_NAME}. This indicates whether push
* challenges may be considered when evaluating whether a request should proceed.
*
* A resource can consume a PushChallengeConfig with by annotating a PushChallengeConfig parameter with {@link Extract}
*/
public class PushChallengeConfigProvider implements ValueParamProvider {
/**
* Configures the PushChallengeConfigProvider
*/
public static class PushChallengeConfigFeature implements Feature {
@Override
public boolean configure(FeatureContext context) {
context.register(new AbstractBinder() {
@Override
protected void configure() {
bind(PushChallengeConfigProvider.class)
.to(ValueParamProvider.class)
.in(Singleton.class);
}
});
return true;
}
}
@Override
public Function<ContainerRequest, ?> getValueProvider(final Parameter parameter) {
if (parameter.getRawType().equals(PushChallengeConfig.class)
&& parameter.isAnnotationPresent(Extract.class)) {
return PushChallengeConfig::new;
}
return null;
}
@Override
public PriorityType getPriority() {
return Priority.HIGH;
}
}

View File

@@ -64,4 +64,12 @@ public interface SpamFilter extends ContainerRequestFilter, Managed {
* @return a {@link SpamChecker} controlled by the spam filter
*/
SpamChecker getSpamChecker();
/**
* 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).
*
* @return a {@link ChallengeConstraintChecker} controlled by the spam filter
*/
ChallengeConstraintChecker getChallengeConstraintChecker();
}