Add filter-provided captcha score thresholds

This commit is contained in:
Ravi Khadiwala
2023-03-21 17:26:42 -05:00
committed by ravi-signal
parent a8eb27940d
commit ee53260d72
16 changed files with 280 additions and 50 deletions

View File

@@ -0,0 +1,20 @@
/*
* 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 {
}

View File

@@ -0,0 +1,46 @@
/*
* 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
* SCORE_THRESHOLD_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;
}
}

View File

@@ -0,0 +1,55 @@
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.
*
* 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.
*
* 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;
}
}