Add client challenges for prekey and message rate limiters

This commit is contained in:
Jon Chambers
2021-05-11 17:21:32 -04:00
committed by GitHub
parent 5752853bba
commit 46110d4d65
46 changed files with 2289 additions and 255 deletions

View File

@@ -15,6 +15,7 @@ import io.lettuce.core.ScriptOutputType;
import io.lettuce.core.cluster.SlotHash;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.push.ApnMessage.Type;
import org.whispersystems.textsecuregcm.redis.ClusterLuaScript;
import org.whispersystems.textsecuregcm.redis.FaultTolerantRedisCluster;
import org.whispersystems.textsecuregcm.redis.RedisException;
@@ -192,7 +193,7 @@ public class ApnFallbackManager implements Managed {
return;
}
apnSender.sendMessage(new ApnMessage(apnId, account.getNumber(), device.getId(), true, Optional.empty()));
apnSender.sendMessage(new ApnMessage(apnId, account.getNumber(), device.getId(), true, Type.NOTIFICATION, Optional.empty()));
retry.mark();
}

View File

@@ -12,21 +12,28 @@ import java.util.Optional;
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class ApnMessage {
public static final String APN_NOTIFICATION_PAYLOAD = "{\"aps\":{\"sound\":\"default\",\"alert\":{\"loc-key\":\"APN_Message\"}}}";
public static final String APN_CHALLENGE_PAYLOAD = "{\"aps\":{\"sound\":\"default\",\"alert\":{\"loc-key\":\"APN_Message\"}}, \"challenge\" : \"%s\"}";
public static final long MAX_EXPIRATION = Integer.MAX_VALUE * 1000L;
public enum Type {
NOTIFICATION, CHALLENGE, RATE_LIMIT_CHALLENGE
}
public static final String APN_NOTIFICATION_PAYLOAD = "{\"aps\":{\"sound\":\"default\",\"alert\":{\"loc-key\":\"APN_Message\"}}}";
public static final String APN_CHALLENGE_PAYLOAD = "{\"aps\":{\"sound\":\"default\",\"alert\":{\"loc-key\":\"APN_Message\"}}, \"challenge\" : \"%s\"}";
public static final String APN_RATE_LIMIT_CHALLENGE_PAYLOAD = "{\"aps\":{\"sound\":\"default\",\"alert\":{\"loc-key\":\"APN_Message\"}}, \"rateLimitChallenge\" : \"%s\"}";
public static final long MAX_EXPIRATION = Integer.MAX_VALUE * 1000L;
private final String apnId;
private final String number;
private final long deviceId;
private final boolean isVoip;
private final Type type;
private final Optional<String> challengeData;
public ApnMessage(String apnId, String number, long deviceId, boolean isVoip, Optional<String> challengeData) {
this.apnId = apnId;
this.number = number;
this.deviceId = deviceId;
this.isVoip = isVoip;
public ApnMessage(String apnId, String number, long deviceId, boolean isVoip, Type type, Optional<String> challengeData) {
this.apnId = apnId;
this.number = number;
this.deviceId = deviceId;
this.isVoip = isVoip;
this.type = type;
this.challengeData = challengeData;
}
@@ -39,8 +46,19 @@ public class ApnMessage {
}
public String getMessage() {
if (!challengeData.isPresent()) return APN_NOTIFICATION_PAYLOAD;
else return String.format(APN_CHALLENGE_PAYLOAD, challengeData.get());
switch (type) {
case NOTIFICATION:
return APN_NOTIFICATION_PAYLOAD;
case CHALLENGE:
return String.format(APN_CHALLENGE_PAYLOAD, challengeData.orElseThrow(AssertionError::new));
case RATE_LIMIT_CHALLENGE:
return String.format(APN_RATE_LIMIT_CHALLENGE_PAYLOAD, challengeData.orElseThrow(AssertionError::new));
default:
throw new AssertionError();
}
}
@VisibleForTesting

View File

@@ -5,10 +5,18 @@
package org.whispersystems.textsecuregcm.push;
import static com.codahale.metrics.MetricRegistry.name;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.SharedMetricRegistries;
import com.google.common.annotations.VisibleForTesting;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.gcm.server.Message;
@@ -22,15 +30,6 @@ import org.whispersystems.textsecuregcm.util.Constants;
import org.whispersystems.textsecuregcm.util.SystemMapper;
import org.whispersystems.textsecuregcm.util.Util;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import static com.codahale.metrics.MetricRegistry.name;
public class GCMSender {
private final Logger logger = LoggerFactory.getLogger(GCMSender.class);
@@ -45,6 +44,7 @@ public class GCMSender {
put("receipt", metricRegistry.meter(name(getClass(), "outbound", "receipt")));
put("notification", metricRegistry.meter(name(getClass(), "outbound", "notification")));
put("challenge", metricRegistry.meter(name(getClass(), "outbound", "challenge")));
put("rateLimitChallenge", metricRegistry.meter(name(getClass(), "outbound", "rateLimitChallenge")));
}};
private final AccountsManager accountsManager;
@@ -72,9 +72,10 @@ public class GCMSender {
String key;
switch (message.getType()) {
case NOTIFICATION: key = "notification"; break;
case CHALLENGE: key = "challenge"; break;
default: throw new AssertionError();
case NOTIFICATION: key = "notification"; break;
case CHALLENGE: key = "challenge"; break;
case RATE_LIMIT_CHALLENGE: key = "rateLimitChallenge"; break;
default: throw new AssertionError();
}
Message request = builder.withDataPart(key, message.getData().orElse("")).build();

View File

@@ -12,7 +12,7 @@ import java.util.Optional;
public class GcmMessage {
public enum Type {
NOTIFICATION, CHALLENGE
NOTIFICATION, CHALLENGE, RATE_LIMIT_CHALLENGE
}
private final String gcmId;

View File

@@ -8,6 +8,7 @@ import io.dropwizard.lifecycle.Managed;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tag;
import org.whispersystems.textsecuregcm.metrics.PushLatencyManager;
import org.whispersystems.textsecuregcm.push.ApnMessage.Type;
import org.whispersystems.textsecuregcm.redis.RedisOperation;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.Device;
@@ -131,10 +132,10 @@ public class MessageSender implements Managed {
ApnMessage apnMessage;
if (!Util.isEmpty(device.getVoipApnId())) {
apnMessage = new ApnMessage(device.getVoipApnId(), account.getNumber(), device.getId(), true, Optional.empty());
apnMessage = new ApnMessage(device.getVoipApnId(), account.getNumber(), device.getId(), true, Type.NOTIFICATION, Optional.empty());
RedisOperation.unchecked(() -> apnFallbackManager.schedule(account, device));
} else {
apnMessage = new ApnMessage(device.getApnId(), account.getNumber(), device.getId(), false, Optional.empty());
apnMessage = new ApnMessage(device.getApnId(), account.getNumber(), device.getId(), false, Type.NOTIFICATION, Optional.empty());
}
apnSender.sendMessage(apnMessage);

View File

@@ -6,6 +6,10 @@
package org.whispersystems.textsecuregcm.push;
public class NotPushRegisteredException extends Exception {
public NotPushRegisteredException() {
super();
}
public NotPushRegisteredException(String s) {
super(s);
}