/v1/backup/auth/check endpoint added

This commit is contained in:
Sergey Skrobotov
2023-01-30 15:34:28 -08:00
parent 896e65545e
commit dc8f62a4ad
24 changed files with 1334 additions and 197 deletions

View File

@@ -0,0 +1,111 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.util;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.lang.reflect.Method;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import org.junit.jupiter.api.Test;
public class E164Test {
private static final Validator VALIDATOR = Validation.buildDefaultValidatorFactory().getValidator();
private static final String E164_VALID = "+18005550123";
private static final String E164_INVALID = "1(800)555-0123";
private static final String EMPTY = "";
@SuppressWarnings("FieldCanBeLocal")
private static class Data {
@E164
private final String number;
private Data(final String number) {
this.number = number;
}
}
private static class Methods {
public void foo(@E164 final String number) {
// noop
}
@E164
public String bar() {
return "nevermind";
}
}
private record Rec(@E164 String number) {
}
@Test
public void testRecord() throws Exception {
checkNoViolations(new Rec(E164_VALID));
checkHasViolations(new Rec(E164_INVALID));
checkHasViolations(new Rec(EMPTY));
}
@Test
public void testClassField() throws Exception {
checkNoViolations(new Data(E164_VALID));
checkHasViolations(new Data(E164_INVALID));
checkHasViolations(new Data(EMPTY));
}
@Test
public void testParameters() throws Exception {
final Methods m = new Methods();
final Method foo = Methods.class.getMethod("foo", String.class);
final Set<ConstraintViolation<Methods>> violations1 =
VALIDATOR.forExecutables().validateParameters(m, foo, new Object[] {E164_VALID});
final Set<ConstraintViolation<Methods>> violations2 =
VALIDATOR.forExecutables().validateParameters(m, foo, new Object[] {E164_INVALID});
final Set<ConstraintViolation<Methods>> violations3 =
VALIDATOR.forExecutables().validateParameters(m, foo, new Object[] {EMPTY});
assertTrue(violations1.isEmpty());
assertFalse(violations2.isEmpty());
assertFalse(violations3.isEmpty());
}
@Test
public void testReturnValue() throws Exception {
final Methods m = new Methods();
final Method bar = Methods.class.getMethod("bar");
final Set<ConstraintViolation<Methods>> violations1 =
VALIDATOR.forExecutables().validateReturnValue(m, bar, E164_VALID);
final Set<ConstraintViolation<Methods>> violations2 =
VALIDATOR.forExecutables().validateReturnValue(m, bar, E164_INVALID);
final Set<ConstraintViolation<Methods>> violations3 =
VALIDATOR.forExecutables().validateReturnValue(m, bar, EMPTY);
assertTrue(violations1.isEmpty());
assertFalse(violations2.isEmpty());
assertFalse(violations3.isEmpty());
}
private static <T> void checkNoViolations(final T object) {
final Set<ConstraintViolation<T>> violations = VALIDATOR.validate(object);
assertTrue(violations.isEmpty());
}
private static <T> void checkHasViolations(final T object) {
final Set<ConstraintViolation<T>> violations = VALIDATOR.validate(object);
assertFalse(violations.isEmpty());
}
}

View File

@@ -1,31 +0,0 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.util;
import org.mockito.Mockito;
public final class MockHelper {
private MockHelper() {
// utility class
}
@FunctionalInterface
public interface MockInitializer<T> {
void init(T mock) throws Exception;
}
public static <T> T buildMock(final Class<T> clazz, final MockInitializer<T> initializer) throws RuntimeException {
final T mock = Mockito.mock(clazz);
try {
initializer.init(mock);
} catch (Exception e) {
throw new RuntimeException(e);
}
return mock;
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.util;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import java.time.Duration;
import java.util.Optional;
import org.mockito.Mockito;
import org.whispersystems.textsecuregcm.controllers.RateLimitExceededException;
import org.whispersystems.textsecuregcm.limits.RateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
public final class MockUtils {
private MockUtils() {
// utility class
}
@FunctionalInterface
public interface MockInitializer<T> {
void init(T mock) throws Exception;
}
public static <T> T buildMock(final Class<T> clazz, final MockInitializer<T> initializer) throws RuntimeException {
final T mock = Mockito.mock(clazz);
try {
initializer.init(mock);
} catch (Exception e) {
throw new RuntimeException(e);
}
return mock;
}
public static MutableClock mutableClock(final long timeMillis) {
return new MutableClock(timeMillis);
}
public static void updateRateLimiterResponseToAllow(
final RateLimiters rateLimitersMock,
final RateLimiters.Handle handle,
final String input) {
final RateLimiter mockRateLimiter = Mockito.mock(RateLimiter.class);
doReturn(Optional.of(mockRateLimiter)).when(rateLimitersMock).byHandle(eq(handle));
try {
doNothing().when(mockRateLimiter).validate(eq(input));
} catch (final RateLimitExceededException e) {
throw new RuntimeException(e);
}
}
public static void updateRateLimiterResponseToFail(
final RateLimiters rateLimitersMock,
final RateLimiters.Handle handle,
final String input,
final Duration retryAfter) {
final RateLimiter mockRateLimiter = Mockito.mock(RateLimiter.class);
doReturn(Optional.of(mockRateLimiter)).when(rateLimitersMock).byHandle(eq(handle));
try {
doThrow(new RateLimitExceededException(retryAfter)).when(mockRateLimiter).validate(eq(input));
} catch (final RateLimitExceededException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.util;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
public class MutableClock extends Clock {
private final AtomicReference<Clock> delegate;
public MutableClock(final long timeMillis) {
this(fixedTimeMillis(timeMillis));
}
public MutableClock(final Clock clock) {
this.delegate = new AtomicReference<>(clock);
}
public MutableClock() {
this(Clock.systemUTC());
}
public MutableClock setTimeMillis(final long timeMillis) {
delegate.set(fixedTimeMillis(timeMillis));
return this;
}
public MutableClock incrementMillis(final long incrementMillis) {
return increment(incrementMillis, TimeUnit.MILLISECONDS);
}
public MutableClock incrementSeconds(final long incrementSeconds) {
return increment(incrementSeconds, TimeUnit.SECONDS);
}
public MutableClock increment(final long increment, final TimeUnit timeUnit) {
final long current = delegate.get().instant().toEpochMilli();
delegate.set(fixedTimeMillis(current + timeUnit.toMillis(increment)));
return this;
}
@Override
public ZoneId getZone() {
return delegate.get().getZone();
}
@Override
public Clock withZone(final ZoneId zone) {
return delegate.get().withZone(zone);
}
@Override
public Instant instant() {
return delegate.get().instant();
}
private static Clock fixedTimeMillis(final long timeMillis) {
return Clock.fixed(Instant.ofEpochMilli(timeMillis), ZoneId.of("Etc/UTC"));
}
}