Add `/v1/accounts` and `/v2/keys` to the WebSocket

This commit is contained in:
Chris Eager 2024-01-02 15:51:57 -06:00 committed by GitHub
parent ad6b99be6a
commit 195f23c347
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 290 additions and 691 deletions

View File

@ -22,7 +22,6 @@ import java.util.ServiceLoader;
import java.util.Set; import java.util.Set;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
/** /**
* One of the extension mechanisms of Swagger Core library (OpenAPI processor) is via custom implementations * One of the extension mechanisms of Swagger Core library (OpenAPI processor) is via custom implementations
@ -42,10 +41,6 @@ public class OpenApiExtension extends AbstractOpenAPIExtension {
public static final ResolvedParameter OPTIONAL_AUTHENTICATED_ACCOUNT = new ResolvedParameter(); public static final ResolvedParameter OPTIONAL_AUTHENTICATED_ACCOUNT = new ResolvedParameter();
public static final ResolvedParameter DISABLED_PERMITTED_AUTHENTICATED_ACCOUNT = new ResolvedParameter();
public static final ResolvedParameter OPTIONAL_DISABLED_PERMITTED_AUTHENTICATED_ACCOUNT = new ResolvedParameter();
/** /**
* When parsing endpoint methods, Swagger will treat the first parameter not annotated as header/path/query param * When parsing endpoint methods, Swagger will treat the first parameter not annotated as header/path/query param
* as a request body (and will ignore other not annotated parameters). In our case, this behavior conflicts with * as a request body (and will ignore other not annotated parameters). In our case, this behavior conflicts with
@ -70,18 +65,10 @@ public class OpenApiExtension extends AbstractOpenAPIExtension {
&& simpleType.getRawClass().equals(AuthenticatedAccount.class)) { && simpleType.getRawClass().equals(AuthenticatedAccount.class)) {
return AUTHENTICATED_ACCOUNT; return AUTHENTICATED_ACCOUNT;
} }
if (type instanceof SimpleType simpleType
&& simpleType.getRawClass().equals(DisabledPermittedAuthenticatedAccount.class)) {
return DISABLED_PERMITTED_AUTHENTICATED_ACCOUNT;
}
if (type instanceof SimpleType simpleType if (type instanceof SimpleType simpleType
&& isOptionalOfType(simpleType, AuthenticatedAccount.class)) { && isOptionalOfType(simpleType, AuthenticatedAccount.class)) {
return OPTIONAL_AUTHENTICATED_ACCOUNT; return OPTIONAL_AUTHENTICATED_ACCOUNT;
} }
if (type instanceof SimpleType simpleType
&& isOptionalOfType(simpleType, DisabledPermittedAuthenticatedAccount.class)) {
return OPTIONAL_DISABLED_PERMITTED_AUTHENTICATED_ACCOUNT;
}
} }
return super.extractParameters( return super.extractParameters(

View File

@ -7,9 +7,7 @@ package org.signal.openapi;
import static com.google.common.base.MoreObjects.firstNonNull; import static com.google.common.base.MoreObjects.firstNonNull;
import static org.signal.openapi.OpenApiExtension.AUTHENTICATED_ACCOUNT; import static org.signal.openapi.OpenApiExtension.AUTHENTICATED_ACCOUNT;
import static org.signal.openapi.OpenApiExtension.DISABLED_PERMITTED_AUTHENTICATED_ACCOUNT;
import static org.signal.openapi.OpenApiExtension.OPTIONAL_AUTHENTICATED_ACCOUNT; import static org.signal.openapi.OpenApiExtension.OPTIONAL_AUTHENTICATED_ACCOUNT;
import static org.signal.openapi.OpenApiExtension.OPTIONAL_DISABLED_PERMITTED_AUTHENTICATED_ACCOUNT;
import com.fasterxml.jackson.annotation.JsonView; import com.fasterxml.jackson.annotation.JsonView;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
@ -54,13 +52,13 @@ public class OpenApiReader extends Reader {
final ResolvedParameter resolved = super.getParameters( final ResolvedParameter resolved = super.getParameters(
type, annotations, operation, classConsumes, methodConsumes, jsonViewAnnotation); type, annotations, operation, classConsumes, methodConsumes, jsonViewAnnotation);
if (resolved == AUTHENTICATED_ACCOUNT || resolved == DISABLED_PERMITTED_AUTHENTICATED_ACCOUNT) { if (resolved == AUTHENTICATED_ACCOUNT) {
operation.setSecurity(ImmutableList.<SecurityRequirement>builder() operation.setSecurity(ImmutableList.<SecurityRequirement>builder()
.addAll(firstNonNull(operation.getSecurity(), Collections.emptyList())) .addAll(firstNonNull(operation.getSecurity(), Collections.emptyList()))
.add(new SecurityRequirement().addList(AUTHENTICATED_ACCOUNT_AUTH_SCHEMA)) .add(new SecurityRequirement().addList(AUTHENTICATED_ACCOUNT_AUTH_SCHEMA))
.build()); .build());
} }
if (resolved == OPTIONAL_AUTHENTICATED_ACCOUNT || resolved == OPTIONAL_DISABLED_PERMITTED_AUTHENTICATED_ACCOUNT) { if (resolved == OPTIONAL_AUTHENTICATED_ACCOUNT) {
operation.setSecurity(ImmutableList.<SecurityRequirement>builder() operation.setSecurity(ImmutableList.<SecurityRequirement>builder()
.addAll(firstNonNull(operation.getSecurity(), Collections.emptyList())) .addAll(firstNonNull(operation.getSecurity(), Collections.emptyList()))
.add(new SecurityRequirement().addList(AUTHENTICATED_ACCOUNT_AUTH_SCHEMA)) .add(new SecurityRequirement().addList(AUTHENTICATED_ACCOUNT_AUTH_SCHEMA))

View File

@ -7,12 +7,10 @@ package org.whispersystems.textsecuregcm;
import static com.codahale.metrics.MetricRegistry.name; import static com.codahale.metrics.MetricRegistry.name;
import static java.util.Objects.requireNonNull; import static java.util.Objects.requireNonNull;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthFilter; import io.dropwizard.auth.AuthFilter;
import io.dropwizard.auth.PolymorphicAuthDynamicFeature; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter; import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.auth.basic.BasicCredentials; import io.dropwizard.auth.basic.BasicCredentials;
import io.dropwizard.core.Application; import io.dropwizard.core.Application;
@ -61,10 +59,7 @@ import org.whispersystems.textsecuregcm.attachments.GcsAttachmentGenerator;
import org.whispersystems.textsecuregcm.attachments.TusAttachmentGenerator; import org.whispersystems.textsecuregcm.attachments.TusAttachmentGenerator;
import org.whispersystems.textsecuregcm.auth.AccountAuthenticator; import org.whispersystems.textsecuregcm.auth.AccountAuthenticator;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.BaseAccountAuthenticator;
import org.whispersystems.textsecuregcm.auth.CertificateGenerator; import org.whispersystems.textsecuregcm.auth.CertificateGenerator;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccountAuthenticator;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.auth.PhoneVerificationTokenManager; import org.whispersystems.textsecuregcm.auth.PhoneVerificationTokenManager;
import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager; import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager;
@ -580,8 +575,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
reportMessageManager.addListener(reportedMessageMetricsListener); reportMessageManager.addListener(reportedMessageMetricsListener);
final AccountAuthenticator accountAuthenticator = new AccountAuthenticator(accountsManager); final AccountAuthenticator accountAuthenticator = new AccountAuthenticator(accountsManager);
final DisabledPermittedAccountAuthenticator disabledPermittedAccountAuthenticator = new DisabledPermittedAccountAuthenticator(
accountsManager);
final MessageSender messageSender = new MessageSender(clientPresenceManager, messagesManager, final MessageSender messageSender = new MessageSender(clientPresenceManager, messagesManager,
pushNotificationManager, pushNotificationManager,
@ -686,13 +679,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
config.getClientCdn().getAttachmentUrls(), config.getClientCdn().getAttachmentUrls(),
clock); clock);
AuthFilter<BasicCredentials, AuthenticatedAccount> accountAuthFilter = new BasicCredentialAuthFilter.Builder<AuthenticatedAccount>().setAuthenticator(
accountAuthenticator).buildAuthFilter();
AuthFilter<BasicCredentials, DisabledPermittedAuthenticatedAccount> disabledPermittedAccountAuthFilter = new BasicCredentialAuthFilter.Builder<DisabledPermittedAuthenticatedAccount>().setAuthenticator(
disabledPermittedAccountAuthenticator).buildAuthFilter();
final BasicCredentialAuthenticationInterceptor basicCredentialAuthenticationInterceptor = final BasicCredentialAuthenticationInterceptor basicCredentialAuthenticationInterceptor =
new BasicCredentialAuthenticationInterceptor(new BaseAccountAuthenticator(accountsManager)); new BasicCredentialAuthenticationInterceptor(new AccountAuthenticator(accountsManager));
final ServerBuilder<?> grpcServer = ServerBuilder.forPort(config.getGrpcPort()) final ServerBuilder<?> grpcServer = ServerBuilder.forPort(config.getGrpcPort())
.addService(ServerInterceptors.intercept(new AccountsGrpcService(accountsManager, rateLimiters, usernameHashZkProofVerifier, registrationRecoveryPasswordsManager), basicCredentialAuthenticationInterceptor)) .addService(ServerInterceptors.intercept(new AccountsGrpcService(accountsManager, rateLimiters, usernameHashZkProofVerifier, registrationRecoveryPasswordsManager), basicCredentialAuthenticationInterceptor))
@ -724,14 +712,16 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
environment.lifecycle().manage(new GrpcServerManagedWrapper(grpcServer.build())); environment.lifecycle().manage(new GrpcServerManagedWrapper(grpcServer.build()));
final AuthFilter<BasicCredentials, AuthenticatedAccount> accountAuthFilter =
new BasicCredentialAuthFilter.Builder<AuthenticatedAccount>()
.setAuthenticator(accountAuthenticator)
.buildAuthFilter();
environment.jersey().register(new RequestStatisticsFilter(TrafficSource.HTTP)); environment.jersey().register(new RequestStatisticsFilter(TrafficSource.HTTP));
environment.jersey().register(MultiRecipientMessageProvider.class); environment.jersey().register(MultiRecipientMessageProvider.class);
environment.jersey().register(new MetricsApplicationEventListener(TrafficSource.HTTP, clientReleaseManager)); environment.jersey().register(new MetricsApplicationEventListener(TrafficSource.HTTP, clientReleaseManager));
environment.jersey() environment.jersey().register(new AuthDynamicFeature(accountAuthFilter));
.register(new PolymorphicAuthDynamicFeature<>(ImmutableMap.of(AuthenticatedAccount.class, accountAuthFilter, environment.jersey().register(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class));
DisabledPermittedAuthenticatedAccount.class, disabledPermittedAccountAuthFilter)));
environment.jersey().register(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)));
environment.jersey().register(new WebsocketRefreshApplicationEventListener(accountsManager, clientPresenceManager)); environment.jersey().register(new WebsocketRefreshApplicationEventListener(accountsManager, clientPresenceManager));
environment.jersey().register(new TimestampResponseFilter()); environment.jersey().register(new TimestampResponseFilter());
@ -749,14 +739,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
webSocketEnvironment.jersey().register(new MetricsApplicationEventListener(TrafficSource.WEBSOCKET, clientReleaseManager)); webSocketEnvironment.jersey().register(new MetricsApplicationEventListener(TrafficSource.WEBSOCKET, clientReleaseManager));
webSocketEnvironment.jersey().register(new KeepAliveController(clientPresenceManager)); webSocketEnvironment.jersey().register(new KeepAliveController(clientPresenceManager));
// these should be common, but use @Auth DisabledPermittedAccount, which isnt supported yet on websocket
environment.jersey().register(
new AccountController(accountsManager, rateLimiters,
turnTokenGenerator,
registrationRecoveryPasswordsManager, usernameHashZkProofVerifier));
environment.jersey().register(new KeysController(rateLimiters, keysManager, accountsManager));
boolean registeredSpamFilter = false; boolean registeredSpamFilter = false;
ReportSpamTokenProvider reportSpamTokenProvider = null; ReportSpamTokenProvider reportSpamTokenProvider = null;
@ -804,6 +786,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
} }
final List<Object> commonControllers = Lists.newArrayList( final List<Object> commonControllers = Lists.newArrayList(
new AccountController(accountsManager, rateLimiters, turnTokenGenerator, registrationRecoveryPasswordsManager,
usernameHashZkProofVerifier),
new AccountControllerV2(accountsManager, changeNumberManager, phoneVerificationTokenManager, new AccountControllerV2(accountsManager, changeNumberManager, phoneVerificationTokenManager,
registrationLockVerificationManager, rateLimiters), registrationLockVerificationManager, rateLimiters),
new ArtController(rateLimiters, artCredentialsGenerator), new ArtController(rateLimiters, artCredentialsGenerator),
@ -824,6 +808,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
new DirectoryV2Controller(directoryV2CredentialsGenerator), new DirectoryV2Controller(directoryV2CredentialsGenerator),
new DonationController(clock, zkReceiptOperations, redeemedReceiptsManager, accountsManager, config.getBadges(), new DonationController(clock, zkReceiptOperations, redeemedReceiptsManager, accountsManager, config.getBadges(),
ReceiptCredentialPresentation::new), ReceiptCredentialPresentation::new),
new KeysController(rateLimiters, keysManager, accountsManager),
new MessageController(rateLimiters, messageByteLimitCardinalityEstimator, messageSender, receiptSender, new MessageController(rateLimiters, messageByteLimitCardinalityEstimator, messageSender, receiptSender,
accountsManager, messagesManager, pushNotificationManager, reportMessageManager, accountsManager, messagesManager, pushNotificationManager, reportMessageManager,
multiRecipientMessageExecutor, messageDeliveryScheduler, reportSpamTokenProvider, clientReleaseManager, multiRecipientMessageExecutor, messageDeliveryScheduler, reportSpamTokenProvider, clientReleaseManager,

View File

@ -1,25 +1,155 @@
/* /*
* Copyright 2013-2021 Signal Messenger, LLC * Copyright 2013 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
package org.whispersystems.textsecuregcm.auth; package org.whispersystems.textsecuregcm.auth;
import static com.codahale.metrics.MetricRegistry.name;
import com.google.common.annotations.VisibleForTesting;
import io.dropwizard.auth.Authenticator; import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials; import io.dropwizard.auth.basic.BasicCredentials;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tags;
import java.time.Clock;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Optional; import java.util.Optional;
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager; import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.storage.RefreshingAccountAndDeviceSupplier;
import org.whispersystems.textsecuregcm.util.Pair;
import org.whispersystems.textsecuregcm.util.Util;
public class AccountAuthenticator extends BaseAccountAuthenticator implements public class AccountAuthenticator implements Authenticator<BasicCredentials, AuthenticatedAccount> {
Authenticator<BasicCredentials, AuthenticatedAccount> {
private static final String LEGACY_NAME_PREFIX = "org.whispersystems.textsecuregcm.auth.BaseAccountAuthenticator";
private static final String AUTHENTICATION_COUNTER_NAME = name(LEGACY_NAME_PREFIX, "authentication");
private static final String AUTHENTICATION_SUCCEEDED_TAG_NAME = "succeeded";
private static final String AUTHENTICATION_FAILURE_REASON_TAG_NAME = "reason";
private static final String DAYS_SINCE_LAST_SEEN_DISTRIBUTION_NAME = name(LEGACY_NAME_PREFIX, "daysSinceLastSeen");
private static final String IS_PRIMARY_DEVICE_TAG = "isPrimary";
@VisibleForTesting
static final char DEVICE_ID_SEPARATOR = '.';
private final AccountsManager accountsManager;
private final Clock clock;
public AccountAuthenticator(AccountsManager accountsManager) { public AccountAuthenticator(AccountsManager accountsManager) {
super(accountsManager); this(accountsManager, Clock.systemUTC());
}
@VisibleForTesting
public AccountAuthenticator(AccountsManager accountsManager, Clock clock) {
this.accountsManager = accountsManager;
this.clock = clock;
}
static Pair<String, Byte> getIdentifierAndDeviceId(final String basicUsername) {
final String identifier;
final byte deviceId;
final int deviceIdSeparatorIndex = basicUsername.indexOf(DEVICE_ID_SEPARATOR);
if (deviceIdSeparatorIndex == -1) {
identifier = basicUsername;
deviceId = Device.PRIMARY_ID;
} else {
identifier = basicUsername.substring(0, deviceIdSeparatorIndex);
deviceId = Byte.parseByte(basicUsername.substring(deviceIdSeparatorIndex + 1));
}
return new Pair<>(identifier, deviceId);
} }
@Override @Override
public Optional<AuthenticatedAccount> authenticate(BasicCredentials basicCredentials) { public Optional<AuthenticatedAccount> authenticate(BasicCredentials basicCredentials) {
return super.authenticate(basicCredentials, true); boolean succeeded = false;
String failureReason = null;
try {
final UUID accountUuid;
final byte deviceId;
{
final Pair<String, Byte> identifierAndDeviceId = getIdentifierAndDeviceId(basicCredentials.getUsername());
accountUuid = UUID.fromString(identifierAndDeviceId.first());
deviceId = identifierAndDeviceId.second();
}
Optional<Account> account = accountsManager.getByAccountIdentifier(accountUuid);
if (account.isEmpty()) {
failureReason = "noSuchAccount";
return Optional.empty();
}
Optional<Device> device = account.get().getDevice(deviceId);
if (device.isEmpty()) {
failureReason = "noSuchDevice";
return Optional.empty();
}
SaltedTokenHash deviceSaltedTokenHash = device.get().getAuthTokenHash();
if (deviceSaltedTokenHash.verify(basicCredentials.getPassword())) {
succeeded = true;
Account authenticatedAccount = updateLastSeen(account.get(), device.get());
if (deviceSaltedTokenHash.getVersion() != SaltedTokenHash.CURRENT_VERSION) {
authenticatedAccount = accountsManager.updateDeviceAuthentication(
authenticatedAccount,
device.get(),
SaltedTokenHash.generateFor(basicCredentials.getPassword())); // new credentials have current version
}
return Optional.of(new AuthenticatedAccount(
new RefreshingAccountAndDeviceSupplier(authenticatedAccount, device.get().getId(), accountsManager)));
}
return Optional.empty();
} catch (IllegalArgumentException | InvalidAuthorizationHeaderException iae) {
failureReason = "invalidHeader";
return Optional.empty();
} finally {
Tags tags = Tags.of(
AUTHENTICATION_SUCCEEDED_TAG_NAME, String.valueOf(succeeded));
if (StringUtils.isNotBlank(failureReason)) {
tags = tags.and(AUTHENTICATION_FAILURE_REASON_TAG_NAME, failureReason);
}
Metrics.counter(AUTHENTICATION_COUNTER_NAME, tags).increment();
}
} }
@VisibleForTesting
public Account updateLastSeen(Account account, Device device) {
// compute a non-negative integer between 0 and 86400.
long n = Util.ensureNonNegativeLong(account.getUuid().getLeastSignificantBits());
final long lastSeenOffsetSeconds = n % ChronoUnit.DAYS.getDuration().toSeconds();
// produce a truncated timestamp which is either today at UTC midnight
// or yesterday at UTC midnight, based on per-user randomized offset used.
final long todayInMillisWithOffset = Util.todayInMillisGivenOffsetFromNow(clock,
Duration.ofSeconds(lastSeenOffsetSeconds).negated());
// only update the device's last seen time when it falls behind the truncated timestamp.
// this ensures a few things:
// (1) each account will only update last-seen at most once per day
// (2) these updates will occur throughout the day rather than all occurring at UTC midnight.
if (device.getLastSeen() < todayInMillisWithOffset) {
Metrics.summary(DAYS_SINCE_LAST_SEEN_DISTRIBUTION_NAME, IS_PRIMARY_DEVICE_TAG, String.valueOf(device.isPrimary()))
.record(Duration.ofMillis(todayInMillisWithOffset - device.getLastSeen()).toDays());
return accountsManager.updateDeviceLastSeen(account, device, Util.todayInMillis(clock));
}
return account;
}
} }

View File

@ -31,7 +31,6 @@ import org.whispersystems.textsecuregcm.util.Pair;
* {@link io.dropwizard.auth.Auth} object with a current device list. * {@link io.dropwizard.auth.Auth} object with a current device list.
* *
* @see AuthenticatedAccount * @see AuthenticatedAccount
* @see DisabledPermittedAuthenticatedAccount
*/ */
public class AuthEnablementRefreshRequirementProvider implements WebsocketRefreshRequirementProvider { public class AuthEnablementRefreshRequirementProvider implements WebsocketRefreshRequirementProvider {

View File

@ -1,173 +0,0 @@
/*
* Copyright 2013 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import static com.codahale.metrics.MetricRegistry.name;
import com.google.common.annotations.VisibleForTesting;
import io.dropwizard.auth.basic.BasicCredentials;
import io.micrometer.core.instrument.Metrics;
import io.micrometer.core.instrument.Tags;
import java.time.Clock;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
import java.util.Optional;
import java.util.UUID;
import org.apache.commons.lang3.StringUtils;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device;
import org.whispersystems.textsecuregcm.storage.RefreshingAccountAndDeviceSupplier;
import org.whispersystems.textsecuregcm.util.Pair;
import org.whispersystems.textsecuregcm.util.Util;
public class BaseAccountAuthenticator {
private static final String AUTHENTICATION_COUNTER_NAME = name(BaseAccountAuthenticator.class, "authentication");
private static final String ENABLED_NOT_REQUIRED_AUTHENTICATION_COUNTER_NAME = name(BaseAccountAuthenticator.class,
"enabledNotRequiredAuthentication");
private static final String AUTHENTICATION_SUCCEEDED_TAG_NAME = "succeeded";
private static final String AUTHENTICATION_FAILURE_REASON_TAG_NAME = "reason";
private static final String ENABLED_TAG_NAME = "enabled";
private static final String DAYS_SINCE_LAST_SEEN_DISTRIBUTION_NAME = name(BaseAccountAuthenticator.class, "daysSinceLastSeen");
private static final String IS_PRIMARY_DEVICE_TAG = "isPrimary";
@VisibleForTesting
static final char DEVICE_ID_SEPARATOR = '.';
private final AccountsManager accountsManager;
private final Clock clock;
public BaseAccountAuthenticator(AccountsManager accountsManager) {
this(accountsManager, Clock.systemUTC());
}
@VisibleForTesting
public BaseAccountAuthenticator(AccountsManager accountsManager, Clock clock) {
this.accountsManager = accountsManager;
this.clock = clock;
}
static Pair<String, Byte> getIdentifierAndDeviceId(final String basicUsername) {
final String identifier;
final byte deviceId;
final int deviceIdSeparatorIndex = basicUsername.indexOf(DEVICE_ID_SEPARATOR);
if (deviceIdSeparatorIndex == -1) {
identifier = basicUsername;
deviceId = Device.PRIMARY_ID;
} else {
identifier = basicUsername.substring(0, deviceIdSeparatorIndex);
deviceId = Byte.parseByte(basicUsername.substring(deviceIdSeparatorIndex + 1));
}
return new Pair<>(identifier, deviceId);
}
public Optional<AuthenticatedAccount> authenticate(BasicCredentials basicCredentials, boolean enabledRequired) {
boolean succeeded = false;
String failureReason = null;
try {
final UUID accountUuid;
final byte deviceId;
{
final Pair<String, Byte> identifierAndDeviceId = getIdentifierAndDeviceId(basicCredentials.getUsername());
accountUuid = UUID.fromString(identifierAndDeviceId.first());
deviceId = identifierAndDeviceId.second();
}
Optional<Account> account = accountsManager.getByAccountIdentifier(accountUuid);
if (account.isEmpty()) {
failureReason = "noSuchAccount";
return Optional.empty();
}
Optional<Device> device = account.get().getDevice(deviceId);
if (device.isEmpty()) {
failureReason = "noSuchDevice";
return Optional.empty();
}
if (enabledRequired) {
final boolean deviceDisabled = !device.get().isEnabled();
if (deviceDisabled) {
failureReason = "deviceDisabled";
}
final boolean accountDisabled = !account.get().isEnabled();
if (accountDisabled) {
failureReason = "accountDisabled";
}
if (accountDisabled || deviceDisabled) {
return Optional.empty();
}
} else {
Metrics.counter(ENABLED_NOT_REQUIRED_AUTHENTICATION_COUNTER_NAME,
ENABLED_TAG_NAME, String.valueOf(device.get().isEnabled() && account.get().isEnabled()),
IS_PRIMARY_DEVICE_TAG, String.valueOf(device.get().isPrimary()))
.increment();
}
SaltedTokenHash deviceSaltedTokenHash = device.get().getAuthTokenHash();
if (deviceSaltedTokenHash.verify(basicCredentials.getPassword())) {
succeeded = true;
Account authenticatedAccount = updateLastSeen(account.get(), device.get());
if (deviceSaltedTokenHash.getVersion() != SaltedTokenHash.CURRENT_VERSION) {
authenticatedAccount = accountsManager.updateDeviceAuthentication(
authenticatedAccount,
device.get(),
SaltedTokenHash.generateFor(basicCredentials.getPassword())); // new credentials have current version
}
return Optional.of(new AuthenticatedAccount(
new RefreshingAccountAndDeviceSupplier(authenticatedAccount, device.get().getId(), accountsManager)));
}
return Optional.empty();
} catch (IllegalArgumentException | InvalidAuthorizationHeaderException iae) {
failureReason = "invalidHeader";
return Optional.empty();
} finally {
Tags tags = Tags.of(
AUTHENTICATION_SUCCEEDED_TAG_NAME, String.valueOf(succeeded));
if (StringUtils.isNotBlank(failureReason)) {
tags = tags.and(AUTHENTICATION_FAILURE_REASON_TAG_NAME, failureReason);
}
Metrics.counter(AUTHENTICATION_COUNTER_NAME, tags).increment();
}
}
@VisibleForTesting
public Account updateLastSeen(Account account, Device device) {
// compute a non-negative integer between 0 and 86400.
long n = Util.ensureNonNegativeLong(account.getUuid().getLeastSignificantBits());
final long lastSeenOffsetSeconds = n % ChronoUnit.DAYS.getDuration().toSeconds();
// produce a truncated timestamp which is either today at UTC midnight
// or yesterday at UTC midnight, based on per-user randomized offset used.
final long todayInMillisWithOffset = Util.todayInMillisGivenOffsetFromNow(clock, Duration.ofSeconds(lastSeenOffsetSeconds).negated());
// only update the device's last seen time when it falls behind the truncated timestamp.
// this ensure a few things:
// (1) each account will only update last-seen at most once per day
// (2) these updates will occur throughout the day rather than all occurring at UTC midnight.
if (device.getLastSeen() < todayInMillisWithOffset) {
Metrics.summary(DAYS_SINCE_LAST_SEEN_DISTRIBUTION_NAME, IS_PRIMARY_DEVICE_TAG, String.valueOf(device.isPrimary()))
.record(Duration.ofMillis(todayInMillisWithOffset - device.getLastSeen()).toDays());
return accountsManager.updateDeviceLastSeen(account, device, Util.todayInMillis(clock));
}
return account;
}
}

View File

@ -62,7 +62,7 @@ public class BasicAuthorizationHeader {
final byte deviceId; final byte deviceId;
{ {
final Pair<String, Byte> identifierAndDeviceId = final Pair<String, Byte> identifierAndDeviceId =
BaseAccountAuthenticator.getIdentifierAndDeviceId(usernameComponent); AccountAuthenticator.getIdentifierAndDeviceId(usernameComponent);
username = identifierAndDeviceId.first(); username = identifierAndDeviceId.first();
deviceId = identifierAndDeviceId.second(); deviceId = identifierAndDeviceId.second();

View File

@ -1,26 +0,0 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
import java.util.Optional;
import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager;
import org.whispersystems.textsecuregcm.storage.AccountsManager;
public class DisabledPermittedAccountAuthenticator extends BaseAccountAuthenticator implements
Authenticator<BasicCredentials, DisabledPermittedAuthenticatedAccount> {
public DisabledPermittedAccountAuthenticator(AccountsManager accountsManager) {
super(accountsManager);
}
@Override
public Optional<DisabledPermittedAuthenticatedAccount> authenticate(BasicCredentials credentials) {
Optional<AuthenticatedAccount> account = super.authenticate(credentials, false);
return account.map(DisabledPermittedAuthenticatedAccount::new);
}
}

View File

@ -1,42 +0,0 @@
/*
* Copyright 2013-2021 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.whispersystems.textsecuregcm.auth;
import java.security.Principal;
import javax.security.auth.Subject;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.Device;
public class DisabledPermittedAuthenticatedAccount implements Principal, AccountAndAuthenticatedDeviceHolder {
private final AuthenticatedAccount authenticatedAccount;
public DisabledPermittedAuthenticatedAccount(final AuthenticatedAccount authenticatedAccount) {
this.authenticatedAccount = authenticatedAccount;
}
@Override
public Account getAccount() {
return authenticatedAccount.getAccount();
}
@Override
public Device getAuthenticatedDevice() {
return authenticatedAccount.getAuthenticatedDevice();
}
// Principal implementation
@Override
public String getName() {
return null;
}
@Override
public boolean implies(Subject subject) {
return false;
}
}

View File

@ -17,7 +17,7 @@ import io.grpc.Status;
import java.util.Optional; import java.util.Optional;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.BaseAccountAuthenticator; import org.whispersystems.textsecuregcm.auth.AccountAuthenticator;
import org.whispersystems.textsecuregcm.util.HeaderUtils; import org.whispersystems.textsecuregcm.util.HeaderUtils;
/** /**
@ -32,11 +32,11 @@ import org.whispersystems.textsecuregcm.util.HeaderUtils;
* intended to be replaced with a more robust and efficient strategy before widespread client adoption. * intended to be replaced with a more robust and efficient strategy before widespread client adoption.
* *
* @see AuthenticationUtil * @see AuthenticationUtil
* @see BaseAccountAuthenticator * @see AccountAuthenticator
*/ */
public class BasicCredentialAuthenticationInterceptor implements ServerInterceptor { public class BasicCredentialAuthenticationInterceptor implements ServerInterceptor {
private final BaseAccountAuthenticator baseAccountAuthenticator; private final AccountAuthenticator accountAuthenticator;
@VisibleForTesting @VisibleForTesting
static final Metadata.Key<String> BASIC_CREDENTIALS = static final Metadata.Key<String> BASIC_CREDENTIALS =
@ -44,8 +44,8 @@ public class BasicCredentialAuthenticationInterceptor implements ServerIntercept
private static final Metadata EMPTY_TRAILERS = new Metadata(); private static final Metadata EMPTY_TRAILERS = new Metadata();
public BasicCredentialAuthenticationInterceptor(final BaseAccountAuthenticator baseAccountAuthenticator) { public BasicCredentialAuthenticationInterceptor(final AccountAuthenticator accountAuthenticator) {
this.baseAccountAuthenticator = baseAccountAuthenticator; this.accountAuthenticator = accountAuthenticator;
} }
@Override @Override
@ -62,7 +62,7 @@ public class BasicCredentialAuthenticationInterceptor implements ServerIntercept
call.close(Status.UNAUTHENTICATED.withDescription("Could not parse credentials"), EMPTY_TRAILERS); call.close(Status.UNAUTHENTICATED.withDescription("Could not parse credentials"), EMPTY_TRAILERS);
} else { } else {
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount = final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
baseAccountAuthenticator.authenticate(maybeCredentials.get(), false); accountAuthenticator.authenticate(maybeCredentials.get());
if (maybeAuthenticatedAccount.isPresent()) { if (maybeAuthenticatedAccount.isPresent()) {
final AuthenticatedAccount authenticatedAccount = maybeAuthenticatedAccount.get(); final AuthenticatedAccount authenticatedAccount = maybeAuthenticatedAccount.get();

View File

@ -4,10 +4,7 @@
*/ */
package org.whispersystems.textsecuregcm.controllers; package org.whispersystems.textsecuregcm.controllers;
import static org.whispersystems.textsecuregcm.metrics.MetricsUtil.name;
import io.dropwizard.auth.Auth; import io.dropwizard.auth.Auth;
import io.micrometer.core.instrument.Metrics;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponse;
@ -38,7 +35,6 @@ import org.signal.libsignal.usernames.BaseUsernameException;
import org.whispersystems.textsecuregcm.auth.AccountAndAuthenticatedDeviceHolder; import org.whispersystems.textsecuregcm.auth.AccountAndAuthenticatedDeviceHolder;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.ChangesDeviceEnabledState; import org.whispersystems.textsecuregcm.auth.ChangesDeviceEnabledState;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash; import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
import org.whispersystems.textsecuregcm.auth.TurnToken; import org.whispersystems.textsecuregcm.auth.TurnToken;
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator; import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
@ -59,7 +55,6 @@ import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
import org.whispersystems.textsecuregcm.identity.ServiceIdentifier; import org.whispersystems.textsecuregcm.identity.ServiceIdentifier;
import org.whispersystems.textsecuregcm.limits.RateLimitedByIp; import org.whispersystems.textsecuregcm.limits.RateLimitedByIp;
import org.whispersystems.textsecuregcm.limits.RateLimiters; import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.metrics.UserAgentTagUtil;
import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.AccountsManager; import org.whispersystems.textsecuregcm.storage.AccountsManager;
import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.storage.Device;
@ -79,7 +74,6 @@ public class AccountController {
public static final int USERNAME_HASH_LENGTH = 32; public static final int USERNAME_HASH_LENGTH = 32;
public static final int MAXIMUM_USERNAME_CIPHERTEXT_LENGTH = 128; public static final int MAXIMUM_USERNAME_CIPHERTEXT_LENGTH = 128;
private static final String INVALID_REGISTRATION_ID = name(AccountController.class, "invalidRegistrationId");
private final AccountsManager accounts; private final AccountsManager accounts;
private final RateLimiters rateLimiters; private final RateLimiters rateLimiters;
private final TurnTokenGenerator turnTokenGenerator; private final TurnTokenGenerator turnTokenGenerator;
@ -112,11 +106,11 @@ public class AccountController {
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ChangesDeviceEnabledState @ChangesDeviceEnabledState
public void setGcmRegistrationId(@Auth DisabledPermittedAuthenticatedAccount disabledPermittedAuth, public void setGcmRegistrationId(@Auth AuthenticatedAccount auth,
@NotNull @Valid GcmRegistrationId registrationId) { @NotNull @Valid GcmRegistrationId registrationId) {
final Account account = disabledPermittedAuth.getAccount(); final Account account = auth.getAccount();
final Device device = disabledPermittedAuth.getAuthenticatedDevice(); final Device device = auth.getAuthenticatedDevice();
if (Objects.equals(device.getGcmId(), registrationId.gcmRegistrationId())) { if (Objects.equals(device.getGcmId(), registrationId.gcmRegistrationId())) {
return; return;
@ -133,9 +127,9 @@ public class AccountController {
@DELETE @DELETE
@Path("/gcm/") @Path("/gcm/")
@ChangesDeviceEnabledState @ChangesDeviceEnabledState
public void deleteGcmRegistrationId(@Auth DisabledPermittedAuthenticatedAccount disabledPermittedAuth) { public void deleteGcmRegistrationId(@Auth AuthenticatedAccount auth) {
Account account = disabledPermittedAuth.getAccount(); Account account = auth.getAccount();
Device device = disabledPermittedAuth.getAuthenticatedDevice(); Device device = auth.getAuthenticatedDevice();
accounts.updateDevice(account, device.getId(), d -> { accounts.updateDevice(account, device.getId(), d -> {
d.setGcmId(null); d.setGcmId(null);
@ -149,11 +143,11 @@ public class AccountController {
@Consumes(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ChangesDeviceEnabledState @ChangesDeviceEnabledState
public void setApnRegistrationId(@Auth DisabledPermittedAuthenticatedAccount disabledPermittedAuth, public void setApnRegistrationId(@Auth AuthenticatedAccount auth,
@NotNull @Valid ApnRegistrationId registrationId) { @NotNull @Valid ApnRegistrationId registrationId) {
final Account account = disabledPermittedAuth.getAccount(); final Account account = auth.getAccount();
final Device device = disabledPermittedAuth.getAuthenticatedDevice(); final Device device = auth.getAuthenticatedDevice();
if (Objects.equals(device.getApnId(), registrationId.apnRegistrationId()) && if (Objects.equals(device.getApnId(), registrationId.apnRegistrationId()) &&
Objects.equals(device.getVoipApnId(), registrationId.voipRegistrationId())) { Objects.equals(device.getVoipApnId(), registrationId.voipRegistrationId())) {
@ -172,9 +166,9 @@ public class AccountController {
@DELETE @DELETE
@Path("/apn/") @Path("/apn/")
@ChangesDeviceEnabledState @ChangesDeviceEnabledState
public void deleteApnRegistrationId(@Auth DisabledPermittedAuthenticatedAccount disabledPermittedAuth) { public void deleteApnRegistrationId(@Auth AuthenticatedAccount auth) {
Account account = disabledPermittedAuth.getAccount(); Account account = auth.getAccount();
Device device = disabledPermittedAuth.getAuthenticatedDevice(); Device device = auth.getAuthenticatedDevice();
accounts.updateDevice(account, device.getId(), d -> { accounts.updateDevice(account, device.getId(), d -> {
d.setApnId(null); d.setApnId(null);
@ -206,9 +200,9 @@ public class AccountController {
@PUT @PUT
@Path("/name/") @Path("/name/")
public void setName(@Auth DisabledPermittedAuthenticatedAccount disabledPermittedAuth, @NotNull @Valid DeviceName deviceName) { public void setName(@Auth AuthenticatedAccount auth, @NotNull @Valid DeviceName deviceName) {
Account account = disabledPermittedAuth.getAccount(); Account account = auth.getAccount();
Device device = disabledPermittedAuth.getAuthenticatedDevice(); Device device = auth.getAuthenticatedDevice();
accounts.updateDevice(account, device.getId(), d -> d.setName(deviceName.getDeviceName())); accounts.updateDevice(account, device.getId(), d -> d.setName(deviceName.getDeviceName()));
} }
@ -218,11 +212,11 @@ public class AccountController {
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
@ChangesDeviceEnabledState @ChangesDeviceEnabledState
public void setAccountAttributes( public void setAccountAttributes(
@Auth DisabledPermittedAuthenticatedAccount disabledPermittedAuth, @Auth AuthenticatedAccount auth,
@HeaderParam(HeaderUtils.X_SIGNAL_AGENT) String userAgent, @HeaderParam(HeaderUtils.X_SIGNAL_AGENT) String userAgent,
@NotNull @Valid AccountAttributes attributes) { @NotNull @Valid AccountAttributes attributes) {
final Account account = disabledPermittedAuth.getAccount(); final Account account = auth.getAccount();
final byte deviceId = disabledPermittedAuth.getAuthenticatedDevice().getId(); final byte deviceId = auth.getAuthenticatedDevice().getId();
final Account updatedAccount = accounts.update(account, a -> { final Account updatedAccount = accounts.update(account, a -> {
a.getDevice(deviceId).ifPresent(d -> { a.getDevice(deviceId).ifPresent(d -> {
@ -246,8 +240,9 @@ public class AccountController {
@GET @GET
@Path("/me") @Path("/me")
@Deprecated() // use whoami
@Produces(MediaType.APPLICATION_JSON) @Produces(MediaType.APPLICATION_JSON)
public AccountIdentityResponse getMe(@Auth DisabledPermittedAuthenticatedAccount auth) { public AccountIdentityResponse getMe(@Auth AuthenticatedAccount auth) {
return buildAccountIdentityResponse(auth); return buildAccountIdentityResponse(auth);
} }
@ -521,7 +516,7 @@ public class AccountController {
@DELETE @DELETE
@Path("/me") @Path("/me")
public CompletableFuture<Response> deleteAccount(@Auth DisabledPermittedAuthenticatedAccount auth) throws InterruptedException { public CompletableFuture<Response> deleteAccount(@Auth AuthenticatedAccount auth) {
return accounts.delete(auth.getAccount(), AccountsManager.DeletionReason.USER_REQUEST).thenApply(Util.ASYNC_EMPTY_RESPONSE); return accounts.delete(auth.getAccount(), AccountsManager.DeletionReason.USER_REQUEST).thenApply(Util.ASYNC_EMPTY_RESPONSE);
} }

View File

@ -42,7 +42,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.auth.Anonymous; import org.whispersystems.textsecuregcm.auth.Anonymous;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.OptionalAccess; import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.entities.ECSignedPreKey; import org.whispersystems.textsecuregcm.entities.ECSignedPreKey;
import org.whispersystems.textsecuregcm.entities.KEMSignedPreKey; import org.whispersystems.textsecuregcm.entities.KEMSignedPreKey;
@ -114,7 +113,7 @@ public class KeysController {
@ApiResponse(responseCode = "401", description = "Account authentication check failed.") @ApiResponse(responseCode = "401", description = "Account authentication check failed.")
@ApiResponse(responseCode = "403", description = "Attempt to change identity key from a non-primary device.") @ApiResponse(responseCode = "403", description = "Attempt to change identity key from a non-primary device.")
@ApiResponse(responseCode = "422", description = "Invalid request format.") @ApiResponse(responseCode = "422", description = "Invalid request format.")
public CompletableFuture<Response> setKeys(@Auth final DisabledPermittedAuthenticatedAccount disabledPermittedAuth, public CompletableFuture<Response> setKeys(@Auth final AuthenticatedAccount auth,
@RequestBody @NotNull @Valid final SetKeysRequest setKeysRequest, @RequestBody @NotNull @Valid final SetKeysRequest setKeysRequest,
@Parameter(allowEmptyValue=true) @Parameter(allowEmptyValue=true)
@ -124,8 +123,8 @@ public class KeysController {
description="whether this operation applies to the account (aci) or phone-number (pni) identity") description="whether this operation applies to the account (aci) or phone-number (pni) identity")
@QueryParam("identity") @DefaultValue("aci") final IdentityType identityType) { @QueryParam("identity") @DefaultValue("aci") final IdentityType identityType) {
final Account account = disabledPermittedAuth.getAccount(); final Account account = auth.getAccount();
final Device device = disabledPermittedAuth.getAuthenticatedDevice(); final Device device = auth.getAuthenticatedDevice();
final UUID identifier = account.getIdentifier(identityType); final UUID identifier = account.getIdentifier(identityType);
checkSignedPreKeySignatures(setKeysRequest, account.getIdentityKey(identityType)); checkSignedPreKeySignatures(setKeysRequest, account.getIdentityKey(identityType));

View File

@ -38,7 +38,7 @@ import org.whispersystems.textsecuregcm.tests.util.AccountsHelper;
import org.whispersystems.textsecuregcm.util.Pair; import org.whispersystems.textsecuregcm.util.Pair;
import org.whispersystems.textsecuregcm.util.TestClock; import org.whispersystems.textsecuregcm.util.TestClock;
class BaseAccountAuthenticatorTest { class AccountAuthenticatorTest {
private final long today = 1590451200000L; private final long today = 1590451200000L;
private final long yesterday = today - 86_400_000L; private final long yesterday = today - 86_400_000L;
@ -46,7 +46,7 @@ class BaseAccountAuthenticatorTest {
private final long currentTime = today + 68_000_000L; private final long currentTime = today + 68_000_000L;
private AccountsManager accountsManager; private AccountsManager accountsManager;
private BaseAccountAuthenticator baseAccountAuthenticator; private AccountAuthenticator accountAuthenticator;
private TestClock clock; private TestClock clock;
private Account acct1; private Account acct1;
private Account acct2; private Account acct2;
@ -56,7 +56,7 @@ class BaseAccountAuthenticatorTest {
void setup() { void setup() {
accountsManager = mock(AccountsManager.class); accountsManager = mock(AccountsManager.class);
clock = TestClock.now(); clock = TestClock.now();
baseAccountAuthenticator = new BaseAccountAuthenticator(accountsManager, clock); accountAuthenticator = new AccountAuthenticator(accountsManager, clock);
// We use static UUIDs here because the UUID affects the "date last seen" offset // We use static UUIDs here because the UUID affects the "date last seen" offset
acct1 = AccountsHelper.generateTestAccount("+14088675309", UUID.fromString("c139cb3e-f70c-4460-b221-815e8bdf778f"), UUID.randomUUID(), List.of(generateTestDevice(yesterday)), null); acct1 = AccountsHelper.generateTestAccount("+14088675309", UUID.fromString("c139cb3e-f70c-4460-b221-815e8bdf778f"), UUID.randomUUID(), List.of(generateTestDevice(yesterday)), null);
@ -81,8 +81,8 @@ class BaseAccountAuthenticatorTest {
final Device device1 = acct1.getDevices().stream().findFirst().get(); final Device device1 = acct1.getDevices().stream().findFirst().get();
final Device device2 = acct2.getDevices().stream().findFirst().get(); final Device device2 = acct2.getDevices().stream().findFirst().get();
final Account updatedAcct1 = baseAccountAuthenticator.updateLastSeen(acct1, device1); final Account updatedAcct1 = accountAuthenticator.updateLastSeen(acct1, device1);
final Account updatedAcct2 = baseAccountAuthenticator.updateLastSeen(acct2, device2); final Account updatedAcct2 = accountAuthenticator.updateLastSeen(acct2, device2);
verify(accountsManager, never()).updateDeviceLastSeen(eq(acct1), any(), anyLong()); verify(accountsManager, never()).updateDeviceLastSeen(eq(acct1), any(), anyLong());
verify(accountsManager).updateDeviceLastSeen(eq(acct2), eq(device2), anyLong()); verify(accountsManager).updateDeviceLastSeen(eq(acct2), eq(device2), anyLong());
@ -101,8 +101,8 @@ class BaseAccountAuthenticatorTest {
final Device device1 = acct1.getDevices().stream().findFirst().get(); final Device device1 = acct1.getDevices().stream().findFirst().get();
final Device device2 = acct2.getDevices().stream().findFirst().get(); final Device device2 = acct2.getDevices().stream().findFirst().get();
final Account updatedAcct1 = baseAccountAuthenticator.updateLastSeen(acct1, device1); final Account updatedAcct1 = accountAuthenticator.updateLastSeen(acct1, device1);
final Account updatedAcct2 = baseAccountAuthenticator.updateLastSeen(acct2, device2); final Account updatedAcct2 = accountAuthenticator.updateLastSeen(acct2, device2);
verify(accountsManager, never()).updateDeviceLastSeen(eq(acct1), any(), anyLong()); verify(accountsManager, never()).updateDeviceLastSeen(eq(acct1), any(), anyLong());
verify(accountsManager, never()).updateDeviceLastSeen(eq(acct2), any(), anyLong()); verify(accountsManager, never()).updateDeviceLastSeen(eq(acct2), any(), anyLong());
@ -121,8 +121,8 @@ class BaseAccountAuthenticatorTest {
final Device device1 = acct1.getDevices().stream().findFirst().get(); final Device device1 = acct1.getDevices().stream().findFirst().get();
final Device device2 = acct2.getDevices().stream().findFirst().get(); final Device device2 = acct2.getDevices().stream().findFirst().get();
final Account updatedAcct1 = baseAccountAuthenticator.updateLastSeen(acct1, device1); final Account updatedAcct1 = accountAuthenticator.updateLastSeen(acct1, device1);
final Account updatedAcct2 = baseAccountAuthenticator.updateLastSeen(acct2, device2); final Account updatedAcct2 = accountAuthenticator.updateLastSeen(acct2, device2);
verify(accountsManager).updateDeviceLastSeen(eq(acct1), eq(device1), anyLong()); verify(accountsManager).updateDeviceLastSeen(eq(acct1), eq(device1), anyLong());
verify(accountsManager).updateDeviceLastSeen(eq(acct2), eq(device2), anyLong()); verify(accountsManager).updateDeviceLastSeen(eq(acct2), eq(device2), anyLong());
@ -140,7 +140,7 @@ class BaseAccountAuthenticatorTest {
final Device device = oldAccount.getDevices().stream().findFirst().get(); final Device device = oldAccount.getDevices().stream().findFirst().get();
baseAccountAuthenticator.updateLastSeen(oldAccount, device); accountAuthenticator.updateLastSeen(oldAccount, device);
verify(accountsManager).updateDeviceLastSeen(eq(oldAccount), eq(device), anyLong()); verify(accountsManager).updateDeviceLastSeen(eq(oldAccount), eq(device), anyLong());
@ -169,7 +169,7 @@ class BaseAccountAuthenticatorTest {
when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION); when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION);
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount = final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), password), true); accountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), password));
assertThat(maybeAuthenticatedAccount).isPresent(); assertThat(maybeAuthenticatedAccount).isPresent();
assertThat(maybeAuthenticatedAccount.get().getAccount().getUuid()).isEqualTo(uuid); assertThat(maybeAuthenticatedAccount.get().getAccount().getUuid()).isEqualTo(uuid);
@ -199,7 +199,7 @@ class BaseAccountAuthenticatorTest {
when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION); when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION);
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount = final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid + "." + deviceId, password), true); accountAuthenticator.authenticate(new BasicCredentials(uuid + "." + deviceId, password));
assertThat(maybeAuthenticatedAccount).isPresent(); assertThat(maybeAuthenticatedAccount).isPresent();
assertThat(maybeAuthenticatedAccount.get().getAccount().getUuid()).isEqualTo(uuid); assertThat(maybeAuthenticatedAccount.get().getAccount().getUuid()).isEqualTo(uuid);
@ -208,8 +208,7 @@ class BaseAccountAuthenticatorTest {
} }
@CartesianTest @CartesianTest
void testAuthenticateEnabledRequired( void testAuthenticateEnabled(
@CartesianTest.Values(booleans = {true, false}) final boolean enabledRequired,
@CartesianTest.Values(booleans = {true, false}) final boolean accountEnabled, @CartesianTest.Values(booleans = {true, false}) final boolean accountEnabled,
@CartesianTest.Values(booleans = {true, false}) final boolean deviceEnabled, @CartesianTest.Values(booleans = {true, false}) final boolean deviceEnabled,
@CartesianTest.Values(booleans = {true, false}) final boolean authenticatedDeviceIsPrimary) { @CartesianTest.Values(booleans = {true, false}) final boolean authenticatedDeviceIsPrimary) {
@ -236,18 +235,15 @@ class BaseAccountAuthenticatorTest {
if (authenticatedDeviceIsPrimary) { if (authenticatedDeviceIsPrimary) {
identifier = uuid.toString(); identifier = uuid.toString();
} else { } else {
identifier = uuid.toString() + BaseAccountAuthenticator.DEVICE_ID_SEPARATOR + deviceId; identifier = uuid.toString() + AccountAuthenticator.DEVICE_ID_SEPARATOR + deviceId;
} }
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount = final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
baseAccountAuthenticator.authenticate(new BasicCredentials(identifier, password), enabledRequired); accountAuthenticator.authenticate(new BasicCredentials(identifier, password));
assertThat(maybeAuthenticatedAccount).isPresent();
assertThat(maybeAuthenticatedAccount.get().getAccount().getUuid()).isEqualTo(uuid);
assertThat(maybeAuthenticatedAccount.get().getAuthenticatedDevice()).isEqualTo(authenticatedDevice);
if (enabledRequired && !(accountEnabled && deviceEnabled)) {
assertThat(maybeAuthenticatedAccount).isEmpty();
} else {
assertThat(maybeAuthenticatedAccount).isPresent();
assertThat(maybeAuthenticatedAccount.get().getAccount().getUuid()).isEqualTo(uuid);
assertThat(maybeAuthenticatedAccount.get().getAuthenticatedDevice()).isEqualTo(authenticatedDevice);
}
} }
@Test @Test
@ -272,7 +268,7 @@ class BaseAccountAuthenticatorTest {
when(credentials.getVersion()).thenReturn(SaltedTokenHash.Version.V1); when(credentials.getVersion()).thenReturn(SaltedTokenHash.Version.V1);
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount = final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), password), true); accountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), password));
assertThat(maybeAuthenticatedAccount).isPresent(); assertThat(maybeAuthenticatedAccount).isPresent();
assertThat(maybeAuthenticatedAccount.get().getAccount().getUuid()).isEqualTo(uuid); assertThat(maybeAuthenticatedAccount.get().getAccount().getUuid()).isEqualTo(uuid);
@ -283,7 +279,7 @@ class BaseAccountAuthenticatorTest {
} }
@Test @Test
void testAuthenticateAccountNotFound() { void testAuthenticateAccountNotFound() {
assertThat(baseAccountAuthenticator.authenticate(new BasicCredentials(UUID.randomUUID().toString(), "password"), true)) assertThat(accountAuthenticator.authenticate(new BasicCredentials(UUID.randomUUID().toString(), "password")))
.isEmpty(); .isEmpty();
} }
@ -309,7 +305,7 @@ class BaseAccountAuthenticatorTest {
when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION); when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION);
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount = final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid + "." + (deviceId + 1), password), true); accountAuthenticator.authenticate(new BasicCredentials(uuid + "." + (deviceId + 1), password));
assertThat(maybeAuthenticatedAccount).isEmpty(); assertThat(maybeAuthenticatedAccount).isEmpty();
verify(account).getDevice((byte) (deviceId + 1)); verify(account).getDevice((byte) (deviceId + 1));
@ -339,7 +335,7 @@ class BaseAccountAuthenticatorTest {
final String incorrectPassword = password + "incorrect"; final String incorrectPassword = password + "incorrect";
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount = final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), incorrectPassword), true); accountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), incorrectPassword));
assertThat(maybeAuthenticatedAccount).isEmpty(); assertThat(maybeAuthenticatedAccount).isEmpty();
verify(credentials).verify(incorrectPassword); verify(credentials).verify(incorrectPassword);
@ -349,7 +345,7 @@ class BaseAccountAuthenticatorTest {
@MethodSource @MethodSource
void testAuthenticateMalformedCredentials(final String username) { void testAuthenticateMalformedCredentials(final String username) {
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount = assertDoesNotThrow( final Optional<AuthenticatedAccount> maybeAuthenticatedAccount = assertDoesNotThrow(
() -> baseAccountAuthenticator.authenticate(new BasicCredentials(username, "password"), true)); () -> accountAuthenticator.authenticate(new BasicCredentials(username, "password")));
assertThat(maybeAuthenticatedAccount).isEmpty(); assertThat(maybeAuthenticatedAccount).isEmpty();
verify(accountsManager, never()).getByAccountIdentifier(any(UUID.class)); verify(accountsManager, never()).getByAccountIdentifier(any(UUID.class));
@ -367,7 +363,7 @@ class BaseAccountAuthenticatorTest {
@MethodSource @MethodSource
void testGetIdentifierAndDeviceId(final String username, final String expectedIdentifier, void testGetIdentifierAndDeviceId(final String username, final String expectedIdentifier,
final byte expectedDeviceId) { final byte expectedDeviceId) {
final Pair<String, Byte> identifierAndDeviceId = BaseAccountAuthenticator.getIdentifierAndDeviceId(username); final Pair<String, Byte> identifierAndDeviceId = AccountAuthenticator.getIdentifierAndDeviceId(username);
assertEquals(expectedIdentifier, identifierAndDeviceId.first()); assertEquals(expectedIdentifier, identifierAndDeviceId.first());
assertEquals(expectedDeviceId, identifierAndDeviceId.second()); assertEquals(expectedDeviceId, identifierAndDeviceId.second());
@ -389,6 +385,6 @@ class BaseAccountAuthenticatorTest {
}) })
void testGetIdentifierAndDeviceIdMalformed(final String malformedUsername) { void testGetIdentifierAndDeviceIdMalformed(final String malformedUsername) {
assertThrows(IllegalArgumentException.class, assertThrows(IllegalArgumentException.class,
() -> BaseAccountAuthenticator.getIdentifierAndDeviceId(malformedUsername)); () -> AccountAuthenticator.getIdentifierAndDeviceId(malformedUsername));
} }
} }

View File

@ -17,11 +17,9 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.Auth; import io.dropwizard.auth.Auth;
import io.dropwizard.auth.PolymorphicAuthDynamicFeature; import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter; import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.jersey.DropwizardResourceConfig; import io.dropwizard.jersey.DropwizardResourceConfig;
import io.dropwizard.jersey.jackson.JacksonMessageBodyProvider; import io.dropwizard.jersey.jackson.JacksonMessageBodyProvider;
@ -40,7 +38,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -97,12 +94,9 @@ class AuthEnablementRefreshRequirementProviderTest {
new TestPrincipal("test", account, authenticatedDevice)); new TestPrincipal("test", account, authenticatedDevice));
private final ResourceExtension resources = ResourceExtension.builder() private final ResourceExtension resources = ResourceExtension.builder()
.addProvider( .addProvider(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<TestPrincipal>()
new PolymorphicAuthDynamicFeature<>(ImmutableMap.of( .setAuthenticator(c -> principalSupplier.get()).buildAuthFilter()))
TestPrincipal.class, .addProvider(new AuthValueFactoryProvider.Binder<>(TestPrincipal.class))
new BasicCredentialAuthFilter.Builder<TestPrincipal>()
.setAuthenticator(c -> principalSupplier.get()).buildAuthFilter())))
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(ImmutableSet.of(TestPrincipal.class)))
.addProvider(applicationEventListener) .addProvider(applicationEventListener)
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new TestResource()) .addResource(new TestResource())

View File

@ -9,7 +9,6 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -35,7 +34,7 @@ import org.junit.jupiter.params.provider.MethodSource;
import org.signal.chat.rpc.EchoRequest; import org.signal.chat.rpc.EchoRequest;
import org.signal.chat.rpc.EchoServiceGrpc; import org.signal.chat.rpc.EchoServiceGrpc;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.BaseAccountAuthenticator; import org.whispersystems.textsecuregcm.auth.AccountAuthenticator;
import org.whispersystems.textsecuregcm.grpc.EchoServiceImpl; import org.whispersystems.textsecuregcm.grpc.EchoServiceImpl;
import org.whispersystems.textsecuregcm.storage.Account; import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.storage.Device; import org.whispersystems.textsecuregcm.storage.Device;
@ -47,15 +46,15 @@ class BasicCredentialAuthenticationInterceptorTest {
private Server server; private Server server;
private ManagedChannel managedChannel; private ManagedChannel managedChannel;
private BaseAccountAuthenticator baseAccountAuthenticator; private AccountAuthenticator accountAuthenticator;
@BeforeEach @BeforeEach
void setUp() throws IOException { void setUp() throws IOException {
baseAccountAuthenticator = mock(BaseAccountAuthenticator.class); accountAuthenticator = mock(AccountAuthenticator.class);
final BasicCredentialAuthenticationInterceptor authenticationInterceptor = final BasicCredentialAuthenticationInterceptor authenticationInterceptor =
new BasicCredentialAuthenticationInterceptor(baseAccountAuthenticator); new BasicCredentialAuthenticationInterceptor(accountAuthenticator);
final String serverName = InProcessServerBuilder.generateName(); final String serverName = InProcessServerBuilder.generateName();
@ -87,10 +86,10 @@ class BasicCredentialAuthenticationInterceptorTest {
final Device device = mock(Device.class); final Device device = mock(Device.class);
when(device.getId()).thenReturn(Device.PRIMARY_ID); when(device.getId()).thenReturn(Device.PRIMARY_ID);
when(baseAccountAuthenticator.authenticate(any(), anyBoolean())) when(accountAuthenticator.authenticate(any()))
.thenReturn(Optional.of(new AuthenticatedAccount(() -> new Pair<>(account, device)))); .thenReturn(Optional.of(new AuthenticatedAccount(() -> new Pair<>(account, device))));
} else { } else {
when(baseAccountAuthenticator.authenticate(any(), anyBoolean())) when(accountAuthenticator.authenticate(any()))
.thenReturn(Optional.empty()); .thenReturn(Optional.empty());
} }

View File

@ -23,9 +23,8 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableSet;
import com.google.common.net.HttpHeaders; import com.google.common.net.HttpHeaders;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import java.security.SecureRandom; import java.security.SecureRandom;
@ -55,7 +54,6 @@ import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.signal.libsignal.usernames.BaseUsernameException; import org.signal.libsignal.usernames.BaseUsernameException;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash; import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock; import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator; import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
@ -144,10 +142,7 @@ class AccountControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder() private static final ResourceExtension resources = ResourceExtension.builder()
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE) .addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class,
DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new JsonMappingExceptionMapper()) .addProvider(new JsonMappingExceptionMapper())
.addProvider(new RateLimitExceededExceptionMapper()) .addProvider(new RateLimitExceededExceptionMapper())
.addProvider(new ImpossiblePhoneNumberExceptionMapper()) .addProvider(new ImpossiblePhoneNumberExceptionMapper())
@ -231,7 +226,7 @@ class AccountControllerTest {
senderTransfer, senderTransfer,
usernameZkProofVerifier); usernameZkProofVerifier);
clearInvocations(AuthHelper.DISABLED_DEVICE); clearInvocations(AuthHelper.VALID_DEVICE_3_PRIMARY);
} }
@Test @Test
@ -268,31 +263,20 @@ class AccountControllerTest {
assertThat(response.getStatus()).isEqualTo(422); assertThat(response.getStatus()).isEqualTo(422);
} }
@Test
void testSetRegistrationLockDisabled() throws Exception {
Response response =
resources.getJerseyTest()
.target("/v1/accounts/registration_lock/")
.request()
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.put(Entity.json(new RegistrationLock("1234567890123456789012345678901234567890123456789012345678901234")));
assertThat(response.getStatus()).isEqualTo(401);
}
@Test @Test
void testSetGcmId() { void testSetGcmId() {
Response response = Response response =
resources.getJerseyTest() resources.getJerseyTest()
.target("/v1/accounts/gcm/") .target("/v1/accounts/gcm/")
.request() .request()
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD)) .header(HttpHeaders.AUTHORIZATION,
AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_3, AuthHelper.VALID_PASSWORD_3_PRIMARY))
.put(Entity.json(new GcmRegistrationId("z000"))); .put(Entity.json(new GcmRegistrationId("z000")));
assertThat(response.getStatus()).isEqualTo(204); assertThat(response.getStatus()).isEqualTo(204);
verify(AuthHelper.DISABLED_DEVICE, times(1)).setGcmId(eq("z000")); verify(AuthHelper.VALID_DEVICE_3_PRIMARY, times(1)).setGcmId(eq("z000"));
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.DISABLED_ACCOUNT), anyByte(), any()); verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.VALID_ACCOUNT_3), anyByte(), any());
} }
@Test @Test
@ -301,7 +285,8 @@ class AccountControllerTest {
resources.getJerseyTest() resources.getJerseyTest()
.target("/v1/accounts/gcm/") .target("/v1/accounts/gcm/")
.request() .request()
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD)) .header(HttpHeaders.AUTHORIZATION,
AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_3, AuthHelper.VALID_PASSWORD_3_PRIMARY))
.put(Entity.json("{}")); .put(Entity.json("{}"));
assertThat(response.getStatus()).isEqualTo(422); assertThat(response.getStatus()).isEqualTo(422);
@ -314,14 +299,15 @@ class AccountControllerTest {
resources.getJerseyTest() resources.getJerseyTest()
.target("/v1/accounts/apn/") .target("/v1/accounts/apn/")
.request() .request()
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD)) .header(HttpHeaders.AUTHORIZATION,
AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_3, AuthHelper.VALID_PASSWORD_3_PRIMARY))
.put(Entity.json(new ApnRegistrationId("first", "second"))); .put(Entity.json(new ApnRegistrationId("first", "second")));
assertThat(response.getStatus()).isEqualTo(204); assertThat(response.getStatus()).isEqualTo(204);
verify(AuthHelper.DISABLED_DEVICE, times(1)).setApnId(eq("first")); verify(AuthHelper.VALID_DEVICE_3_PRIMARY, times(1)).setApnId(eq("first"));
verify(AuthHelper.DISABLED_DEVICE, times(1)).setVoipApnId(eq("second")); verify(AuthHelper.VALID_DEVICE_3_PRIMARY, times(1)).setVoipApnId(eq("second"));
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.DISABLED_ACCOUNT), anyByte(), any()); verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.VALID_ACCOUNT_3), anyByte(), any());
} }
@Test @Test
@ -330,49 +316,31 @@ class AccountControllerTest {
resources.getJerseyTest() resources.getJerseyTest()
.target("/v1/accounts/apn/") .target("/v1/accounts/apn/")
.request() .request()
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD)) .header(HttpHeaders.AUTHORIZATION,
AuthHelper.getAuthHeader(AuthHelper.VALID_UUID_3, AuthHelper.VALID_PASSWORD_3_PRIMARY))
.put(Entity.json(new ApnRegistrationId("first", null))); .put(Entity.json(new ApnRegistrationId("first", null)));
assertThat(response.getStatus()).isEqualTo(204); assertThat(response.getStatus()).isEqualTo(204);
verify(AuthHelper.DISABLED_DEVICE, times(1)).setApnId(eq("first")); verify(AuthHelper.VALID_DEVICE_3_PRIMARY, times(1)).setApnId(eq("first"));
verify(AuthHelper.DISABLED_DEVICE, times(1)).setVoipApnId(null); verify(AuthHelper.VALID_DEVICE_3_PRIMARY, times(1)).setVoipApnId(null);
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.DISABLED_ACCOUNT), anyByte(), any()); verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.VALID_ACCOUNT_3), anyByte(), any());
} }
@ParameterizedTest @ParameterizedTest
@MethodSource @ValueSource(strings = {"/v1/accounts/whoami", "/v1/accounts/me"})
void testWhoAmI(final String path, final boolean enabledAccount, final int expectedHttpStatusCode) { void testWhoAmI(final String path) {
final UUID aci;
final String password;
if (enabledAccount) {
aci = AuthHelper.VALID_UUID;
password = AuthHelper.VALID_PASSWORD;
} else {
aci = AuthHelper.DISABLED_UUID;
password = AuthHelper.DISABLED_PASSWORD;
}
final Response response = resources.getJerseyTest() final Response response = resources.getJerseyTest()
.target(path) .target(path)
.request() .request()
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(aci, password)) .header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.get(); .get();
assertThat(response.getStatus()).isEqualTo(expectedHttpStatusCode); assertThat(response.getStatus()).isEqualTo(200);
if (expectedHttpStatusCode == 200) { assertThat(response.readEntity(AccountIdentityResponse.class).uuid()).isEqualTo(AuthHelper.VALID_UUID);
assertThat(response.readEntity(AccountIdentityResponse.class).uuid()).isEqualTo(aci);
}
}
static Stream<Arguments> testWhoAmI() {
return Stream.of(
Arguments.of("/v1/accounts/whoami", true, 200),
Arguments.of("/v1/accounts/whoami", false, 401),
Arguments.of("/v1/accounts/me", true, 200),
Arguments.of("/v1/accounts/me", false, 200)
);
} }
static Stream<Arguments> testSetUsernameLink() { static Stream<Arguments> testSetUsernameLink() {

View File

@ -19,10 +19,9 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableSet;
import com.google.i18n.phonenumbers.PhoneNumberUtil; import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber; import com.google.i18n.phonenumbers.Phonenumber;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -65,7 +64,6 @@ import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair; import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.PhoneVerificationTokenManager; import org.whispersystems.textsecuregcm.auth.PhoneVerificationTokenManager;
import org.whispersystems.textsecuregcm.auth.RegistrationLockError; import org.whispersystems.textsecuregcm.auth.RegistrationLockError;
import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager; import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager;
@ -118,10 +116,7 @@ class AccountControllerV2Test {
private final ResourceExtension resources = ResourceExtension.builder() private final ResourceExtension resources = ResourceExtension.builder()
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE) .addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class,
DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new RateLimitExceededExceptionMapper()) .addProvider(new RateLimitExceededExceptionMapper())
.addProvider(new ImpossiblePhoneNumberExceptionMapper()) .addProvider(new ImpossiblePhoneNumberExceptionMapper())
.addProvider(new NonNormalizedPhoneNumberExceptionMapper()) .addProvider(new NonNormalizedPhoneNumberExceptionMapper())

View File

@ -14,8 +14,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset; import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableSet; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import io.grpc.Status; import io.grpc.Status;
@ -52,7 +51,6 @@ import org.signal.libsignal.zkgroup.VerificationFailedException;
import org.signal.libsignal.zkgroup.backups.BackupAuthCredentialPresentation; import org.signal.libsignal.zkgroup.backups.BackupAuthCredentialPresentation;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.AuthenticatedBackupUser; import org.whispersystems.textsecuregcm.auth.AuthenticatedBackupUser;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.backup.BackupAuthManager; import org.whispersystems.textsecuregcm.backup.BackupAuthManager;
import org.whispersystems.textsecuregcm.backup.BackupAuthTestUtil; import org.whispersystems.textsecuregcm.backup.BackupAuthTestUtil;
import org.whispersystems.textsecuregcm.backup.BackupManager; import org.whispersystems.textsecuregcm.backup.BackupManager;
@ -76,8 +74,7 @@ public class ArchiveControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder() private static final ResourceExtension resources = ResourceExtension.builder()
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE) .addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new CompletionExceptionMapper()) .addProvider(new CompletionExceptionMapper())
.addResource(new GrpcStatusRuntimeExceptionMapper()) .addResource(new GrpcStatusRuntimeExceptionMapper())
.addProvider(new RateLimitExceededExceptionMapper()) .addProvider(new RateLimitExceededExceptionMapper())

View File

@ -9,8 +9,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes; import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
import com.google.common.collect.ImmutableSet; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import java.time.Duration; import java.time.Duration;
@ -18,7 +17,6 @@ import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.configuration.ArtServiceConfiguration; import org.whispersystems.textsecuregcm.configuration.ArtServiceConfiguration;
@ -36,8 +34,7 @@ class ArtControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder() private static final ResourceExtension resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.setMapper(SystemMapper.jsonMapper()) .setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new ArtController(rateLimiters, artCredentialsGenerator)) .addResource(new ArtController(rateLimiters, artCredentialsGenerator))

View File

@ -9,8 +9,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableSet; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import java.io.IOException; import java.io.IOException;
@ -26,7 +25,6 @@ import java.security.spec.InvalidKeySpecException;
import java.util.Base64; import java.util.Base64;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.ws.rs.core.Response;
import org.assertj.core.api.Assertions; import org.assertj.core.api.Assertions;
import org.assertj.core.api.Condition; import org.assertj.core.api.Condition;
import org.assertj.core.api.InstanceOfAssertFactories; import org.assertj.core.api.InstanceOfAssertFactories;
@ -37,7 +35,6 @@ import org.whispersystems.textsecuregcm.attachments.GcsAttachmentGenerator;
import org.whispersystems.textsecuregcm.attachments.TusAttachmentGenerator; import org.whispersystems.textsecuregcm.attachments.TusAttachmentGenerator;
import org.whispersystems.textsecuregcm.attachments.TusConfiguration; import org.whispersystems.textsecuregcm.attachments.TusConfiguration;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes; import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
import org.whispersystems.textsecuregcm.entities.AttachmentDescriptorV2; import org.whispersystems.textsecuregcm.entities.AttachmentDescriptorV2;
import org.whispersystems.textsecuregcm.entities.AttachmentDescriptorV3; import org.whispersystems.textsecuregcm.entities.AttachmentDescriptorV3;
@ -93,15 +90,15 @@ class AttachmentControllerTest {
"signal@example.com", 1000, "/attach-here", RSA_PRIVATE_KEY_PEM); "signal@example.com", 1000, "/attach-here", RSA_PRIVATE_KEY_PEM);
resources = ResourceExtension.builder() resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class))) .setMapper(SystemMapper.jsonMapper())
.setMapper(SystemMapper.jsonMapper()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .addResource(new AttachmentControllerV2(RATE_LIMITERS, "accessKey", "accessSecret", "us-east-1",
.addResource(new AttachmentControllerV2(RATE_LIMITERS, "accessKey", "accessSecret", "us-east-1", "attachmentv2-bucket")) "attachmentv2-bucket"))
.addResource(new AttachmentControllerV3(RATE_LIMITERS, gcsAttachmentGenerator)) .addResource(new AttachmentControllerV3(RATE_LIMITERS, gcsAttachmentGenerator))
.addProvider(new AttachmentControllerV4(RATE_LIMITERS, .addProvider(new AttachmentControllerV4(RATE_LIMITERS,
gcsAttachmentGenerator, gcsAttachmentGenerator,
new TusAttachmentGenerator(new TusConfiguration( new SecretBytes(TUS_SECRET), TUS_URL)), new TusAttachmentGenerator(new TusConfiguration(new SecretBytes(TUS_SECRET), TUS_URL)),
EXPERIMENT_MANAGER)) EXPERIMENT_MANAGER))
.build(); .build();
} catch (IOException | InvalidKeyException | InvalidKeySpecException e) { } catch (IOException | InvalidKeyException | InvalidKeySpecException e) {
@ -194,17 +191,6 @@ class AttachmentControllerTest {
assertThat(credentialParts[4]).isEqualTo("goog4_request"); assertThat(credentialParts[4]).isEqualTo("goog4_request");
} }
@Test
void testV3FormDisabled() {
Response response = resources.getJerseyTest()
.target("/v3/attachments/form/upload")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.get();
assertThat(response.getStatus()).isEqualTo(401);
}
@Test @Test
void testV2Form() throws IOException { void testV2Form() throws IOException {
AttachmentDescriptorV2 descriptor = resources.getJerseyTest() AttachmentDescriptorV2 descriptor = resources.getJerseyTest()
@ -233,14 +219,4 @@ class AttachmentControllerTest {
assertThat(new String(Base64.getDecoder().decode(descriptor.policy()))).contains("[\"content-length-range\", 1, 104857600]"); assertThat(new String(Base64.getDecoder().decode(descriptor.policy()))).contains("[\"content-length-range\", 1, 104857600]");
} }
@Test
void testV2FormDisabled() {
Response response = resources.getJerseyTest()
.target("/v2/attachments/form/upload")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.get();
assertThat(response.getStatus()).isEqualTo(401);
}
} }

View File

@ -10,8 +10,7 @@ import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableSet; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import javax.ws.rs.client.Entity; import javax.ws.rs.client.Entity;
@ -24,7 +23,6 @@ import org.signal.libsignal.protocol.util.Hex;
import org.signal.libsignal.zkgroup.GenericServerSecretParams; import org.signal.libsignal.zkgroup.GenericServerSecretParams;
import org.signal.libsignal.zkgroup.calllinks.CreateCallLinkCredentialRequestContext; import org.signal.libsignal.zkgroup.calllinks.CreateCallLinkCredentialRequestContext;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.entities.GetCreateCallLinkCredentialsRequest; import org.whispersystems.textsecuregcm.entities.GetCreateCallLinkCredentialsRequest;
import org.whispersystems.textsecuregcm.limits.RateLimiter; import org.whispersystems.textsecuregcm.limits.RateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiters; import org.whispersystems.textsecuregcm.limits.RateLimiters;
@ -43,8 +41,7 @@ public class CallLinkControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder() private static final ResourceExtension resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new RateLimitExceededExceptionMapper()) .addProvider(new RateLimitExceededExceptionMapper())
.setMapper(SystemMapper.jsonMapper()) .setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())

View File

@ -10,8 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import com.google.common.collect.ImmutableSet; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import java.io.IOException; import java.io.IOException;
@ -41,7 +40,6 @@ import org.signal.libsignal.zkgroup.auth.ServerZkAuthOperations;
import org.signal.libsignal.zkgroup.calllinks.CallLinkAuthCredentialResponse; import org.signal.libsignal.zkgroup.calllinks.CallLinkAuthCredentialResponse;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.CertificateGenerator; import org.whispersystems.textsecuregcm.auth.CertificateGenerator;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.OptionalAccess; import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.entities.DeliveryCertificate; import org.whispersystems.textsecuregcm.entities.DeliveryCertificate;
import org.whispersystems.textsecuregcm.entities.GroupCredentials; import org.whispersystems.textsecuregcm.entities.GroupCredentials;
@ -81,8 +79,7 @@ class CertificateControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder() private static final ResourceExtension resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.setMapper(SystemMapper.jsonMapper()) .setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new CertificateController(certificateGenerator, serverZkAuthOperations, genericServerSecretParams, clock)) .addResource(new CertificateController(certificateGenerator, serverZkAuthOperations, genericServerSecretParams, clock))
@ -207,17 +204,6 @@ class CertificateControllerTest {
assertEquals(response.getStatus(), 401); assertEquals(response.getStatus(), 401);
} }
@Test
void testDisabledAuthentication() {
Response response = resources.getJerseyTest()
.target("/v1/certificate/delivery")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.get();
assertEquals(response.getStatus(), 401);
}
@Test @Test
void testGetSingleGroupCredentialWithPniAsAci() { void testGetSingleGroupCredentialWithPniAsAci() {
final Instant startOfDay = clock.instant().truncatedTo(ChronoUnit.DAYS); final Instant startOfDay = clock.instant().truncatedTo(ChronoUnit.DAYS);

View File

@ -17,13 +17,12 @@ import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.common.net.HttpHeaders; import com.google.common.net.HttpHeaders;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import java.io.IOException; import java.io.IOException;
import java.time.Duration; import java.time.Duration;
import java.util.Optional; import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import javax.ws.rs.client.Entity; import javax.ws.rs.client.Entity;
import javax.ws.rs.container.ContainerRequestContext; import javax.ws.rs.container.ContainerRequestContext;
@ -38,7 +37,6 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource; import org.junit.jupiter.params.provider.ValueSource;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.limits.RateLimitChallengeManager; import org.whispersystems.textsecuregcm.limits.RateLimitChallengeManager;
import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper; import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper;
import org.whispersystems.textsecuregcm.push.NotPushRegisteredException; import org.whispersystems.textsecuregcm.push.NotPushRegisteredException;
@ -60,8 +58,7 @@ class ChallengeControllerTest {
private static final ResourceExtension EXTENSION = ResourceExtension.builder() private static final ResourceExtension EXTENSION = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
Set.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(ScoreThresholdProvider.ScoreThresholdFeature.class) .addProvider(ScoreThresholdProvider.ScoreThresholdFeature.class)
.addProvider(PushChallengeConfigProvider.PushChallengeConfigFeature.class) .addProvider(PushChallengeConfigProvider.PushChallengeConfigFeature.class)
.addProvider(new Feature() { .addProvider(new Feature() {

View File

@ -18,9 +18,8 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableSet;
import com.google.common.net.HttpHeaders; import com.google.common.net.HttpHeaders;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import io.lettuce.core.cluster.api.async.RedisAdvancedClusterAsyncCommands; import io.lettuce.core.cluster.api.async.RedisAdvancedClusterAsyncCommands;
@ -53,7 +52,6 @@ import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair; import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.WebsocketRefreshApplicationEventListener; import org.whispersystems.textsecuregcm.auth.WebsocketRefreshApplicationEventListener;
import org.whispersystems.textsecuregcm.entities.AccountAttributes; import org.whispersystems.textsecuregcm.entities.AccountAttributes;
import org.whispersystems.textsecuregcm.entities.ApnRegistrationId; import org.whispersystems.textsecuregcm.entities.ApnRegistrationId;
@ -117,8 +115,7 @@ class DeviceControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder() private static final ResourceExtension resources = ResourceExtension.builder()
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE) .addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addProvider(new WebsocketRefreshApplicationEventListener(accountsManager, clientPresenceManager)) .addProvider(new WebsocketRefreshApplicationEventListener(accountsManager, clientPresenceManager))
.addProvider(new DeviceLimitExceededExceptionMapper()) .addProvider(new DeviceLimitExceededExceptionMapper())
@ -671,17 +668,6 @@ class DeviceControllerTest {
"incorrect-signature".getBytes(StandardCharsets.UTF_8)); "incorrect-signature".getBytes(StandardCharsets.UTF_8));
} }
@Test
void disabledDeviceRegisterTest() {
Response response = resources.getJerseyTest()
.target("/v1/devices/provisioning/code")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.get();
assertThat(response.getStatus()).isEqualTo(401);
}
@Test @Test
void maxDevicesTest() { void maxDevicesTest() {
final AuthHelper.TestAccount testAccount = AUTH_FILTER_EXTENSION.createTestAccount(); final AuthHelper.TestAccount testAccount = AUTH_FILTER_EXTENSION.createTestAccount();

View File

@ -13,8 +13,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableSet; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import java.time.Clock; import java.time.Clock;
import java.time.Instant; import java.time.Instant;
@ -34,7 +33,6 @@ import org.signal.libsignal.zkgroup.receipts.ReceiptCredentialPresentation;
import org.signal.libsignal.zkgroup.receipts.ReceiptSerial; import org.signal.libsignal.zkgroup.receipts.ReceiptSerial;
import org.signal.libsignal.zkgroup.receipts.ServerZkReceiptOperations; import org.signal.libsignal.zkgroup.receipts.ServerZkReceiptOperations;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.configuration.BadgeConfiguration; import org.whispersystems.textsecuregcm.configuration.BadgeConfiguration;
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration; import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
import org.whispersystems.textsecuregcm.entities.BadgeSvg; import org.whispersystems.textsecuregcm.entities.BadgeSvg;
@ -96,8 +94,7 @@ class DonationControllerTest {
resources = ResourceExtension.builder() resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new DonationController(clock, zkReceiptOperations, redeemedReceiptsManager, accountsManager, .addResource(new DonationController(clock, zkReceiptOperations, redeemedReceiptsManager, accountsManager,
getBadgesConfiguration(), receiptCredentialPresentationFactory)) getBadgesConfiguration(), receiptCredentialPresentationFactory))

View File

@ -22,8 +22,7 @@ import static org.mockito.Mockito.when;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize; import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.google.common.collect.ImmutableSet; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import java.time.Duration; import java.time.Duration;
@ -49,7 +48,6 @@ import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair; import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.OptionalAccess; import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.entities.ECPreKey; import org.whispersystems.textsecuregcm.entities.ECPreKey;
import org.whispersystems.textsecuregcm.entities.ECSignedPreKey; import org.whispersystems.textsecuregcm.entities.ECSignedPreKey;
@ -135,8 +133,7 @@ class KeysControllerTest {
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE) .addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(CompletionExceptionMapper.class) .addProvider(CompletionExceptionMapper.class)
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(ImmutableSet.of( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new ServerRejectedExceptionMapper()) .addResource(new ServerRejectedExceptionMapper())
.addResource(new KeysController(rateLimiters, KEYS, accounts)) .addResource(new KeysController(rateLimiters, KEYS, accounts))
@ -340,18 +337,6 @@ class KeysControllerTest {
verify(accounts).updateDeviceTransactionallyAsync(eq(AuthHelper.VALID_ACCOUNT), anyByte(), any(), any()); verify(accounts).updateDeviceTransactionallyAsync(eq(AuthHelper.VALID_ACCOUNT), anyByte(), any(), any());
} }
@Test
void disabledPutSignedPreKeyV2() {
ECSignedPreKey test = KeysHelper.signedECPreKey(9999, IDENTITY_KEY_PAIR);
Response response = resources.getJerseyTest()
.target("/v2/keys/signed")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.put(Entity.entity(test, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(401);
}
@Test @Test
void validSingleRequestTestV2() { void validSingleRequestTestV2() {
PreKeyResponse result = resources.getJerseyTest() PreKeyResponse result = resources.getJerseyTest()
@ -990,35 +975,4 @@ class KeysControllerTest {
assertThat(response.getStatus()).isEqualTo(422); assertThat(response.getStatus()).isEqualTo(422);
} }
@Test
void disabledPutKeysTestV2() {
final ECPreKey preKey = KeysHelper.ecPreKey(31337);
final ECKeyPair identityKeyPair = Curve.generateKeyPair();
final ECSignedPreKey signedPreKey = KeysHelper.signedECPreKey(31338, identityKeyPair);
final IdentityKey identityKey = new IdentityKey(identityKeyPair.getPublicKey());
when(AuthHelper.DISABLED_ACCOUNT.getIdentityKey(IdentityType.ACI)).thenReturn(identityKey);
final SetKeysRequest setKeysRequest = new SetKeysRequest(List.of(preKey), signedPreKey, null, null);
Response response =
resources.getJerseyTest()
.target("/v2/keys")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.put(Entity.entity(setKeysRequest, MediaType.APPLICATION_JSON_TYPE));
assertThat(response.getStatus()).isEqualTo(204);
ArgumentCaptor<List<ECPreKey>> listCaptor = ArgumentCaptor.forClass(List.class);
verify(KEYS).storeEcOneTimePreKeys(eq(AuthHelper.DISABLED_UUID), eq(SAMPLE_DEVICE_ID), listCaptor.capture());
List<ECPreKey> capturedList = listCaptor.getValue();
assertThat(capturedList.size()).isEqualTo(1);
assertThat(capturedList.get(0).keyId()).isEqualTo(31337);
assertThat(capturedList.get(0).publicKey()).isEqualTo(preKey.publicKey());
verify(AuthHelper.DISABLED_DEVICE).setSignedPreKey(eq(signedPreKey));
verify(accounts).updateDeviceTransactionallyAsync(eq(AuthHelper.DISABLED_ACCOUNT), eq(SAMPLE_DEVICE_ID), any(), any());
}
} }

View File

@ -32,10 +32,9 @@ import static org.whispersystems.textsecuregcm.tests.util.JsonHelpers.jsonFixtur
import static org.whispersystems.textsecuregcm.util.MockUtils.exactly; import static org.whispersystems.textsecuregcm.util.MockUtils.exactly;
import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands; import io.lettuce.core.cluster.api.sync.RedisAdvancedClusterCommands;
@ -85,7 +84,6 @@ import org.mockito.ArgumentCaptor;
import org.signal.libsignal.protocol.ecc.Curve; import org.signal.libsignal.protocol.ecc.Curve;
import org.signal.libsignal.protocol.ecc.ECKeyPair; import org.signal.libsignal.protocol.ecc.ECKeyPair;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.OptionalAccess; import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessUtil; import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessUtil;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration; import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
@ -184,8 +182,7 @@ class MessageControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder() private static final ResourceExtension resources = ResourceExtension.builder()
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE) .addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(RateLimitExceededExceptionMapper.class) .addProvider(RateLimitExceededExceptionMapper.class)
.addProvider(MultiRecipientMessageProvider.class) .addProvider(MultiRecipientMessageProvider.class)
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
@ -283,20 +280,6 @@ class MessageControllerTest {
messageDeliveryScheduler.dispose(); messageDeliveryScheduler.dispose();
} }
@Test
void testSendFromDisabledAccount() throws Exception {
Response response =
resources.getJerseyTest()
.target(String.format("/v1/messages/%s", SINGLE_DEVICE_UUID))
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.put(Entity.entity(SystemMapper.jsonMapper().readValue(jsonFixture("fixtures/current_message_single_device.json"),
IncomingMessageList.class),
MediaType.APPLICATION_JSON_TYPE));
assertThat("Unauthorized response", response.getStatus(), is(equalTo(401)));
}
@Test @Test
void testSingleDeviceCurrent() throws Exception { void testSingleDeviceCurrent() throws Exception {
Response response = Response response =

View File

@ -10,8 +10,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableSet; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -24,7 +23,6 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.currency.CurrencyConversionManager; import org.whispersystems.textsecuregcm.currency.CurrencyConversionManager;
@ -42,8 +40,7 @@ class PaymentsControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder() private static final ResourceExtension resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new PaymentsController(currencyManager, paymentsCredentialsGenerator)) .addResource(new PaymentsController(currencyManager, paymentsCredentialsGenerator))
.build(); .build();
@ -90,17 +87,6 @@ class PaymentsControllerTest {
assertThat(response.getStatus()).isEqualTo(401); assertThat(response.getStatus()).isEqualTo(401);
} }
@Test
void testDisabledGetAuthToken() {
Response response =
resources.getJerseyTest()
.target("/v1/payments/auth")
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.get();
assertThat(response.getStatus()).isEqualTo(401);
}
@Test @Test
void testGetCurrencyConversions() { void testGetCurrencyConversions() {
CurrencyConversionEntityList conversions = CurrencyConversionEntityList conversions =

View File

@ -21,8 +21,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableSet; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -76,7 +75,6 @@ import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredentialRequest;
import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredentialRequestContext; import org.signal.libsignal.zkgroup.profiles.ProfileKeyCredentialRequestContext;
import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations; import org.signal.libsignal.zkgroup.profiles.ServerZkProfileOperations;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.OptionalAccess; import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.configuration.BadgeConfiguration; import org.whispersystems.textsecuregcm.configuration.BadgeConfiguration;
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration; import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
@ -149,8 +147,7 @@ class ProfileControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder() private static final ResourceExtension resources = ResourceExtension.builder()
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE) .addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new RateLimitExceededExceptionMapper()) .addProvider(new RateLimitExceededExceptionMapper())
.setMapper(SystemMapper.jsonMapper()) .setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
@ -384,18 +381,6 @@ class ProfileControllerTest {
assertThat(response.getStatus()).isEqualTo(401); assertThat(response.getStatus()).isEqualTo(401);
} }
@Test
void testProfileGetDisabled() {
final Response response = resources.getJerseyTest()
.target("/v1/profile/" + AuthHelper.VALID_UUID_TWO)
.request()
.header("Authorization", AuthHelper.getAuthHeader(AuthHelper.DISABLED_UUID, AuthHelper.DISABLED_PASSWORD))
.get();
assertThat(response.getStatus()).isEqualTo(401);
}
@Test @Test
void testProfileCapabilities() { void testProfileCapabilities() {
final BaseProfileResponse profile = resources.getJerseyTest() final BaseProfileResponse profile = resources.getJerseyTest()

View File

@ -15,8 +15,7 @@ import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableSet; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
@ -32,7 +31,6 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.entities.ProvisioningMessage; import org.whispersystems.textsecuregcm.entities.ProvisioningMessage;
import org.whispersystems.textsecuregcm.limits.RateLimiter; import org.whispersystems.textsecuregcm.limits.RateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiters; import org.whispersystems.textsecuregcm.limits.RateLimiters;
@ -52,8 +50,7 @@ class ProvisioningControllerTest {
private static final ResourceExtension RESOURCE_EXTENSION = ResourceExtension.builder() private static final ResourceExtension RESOURCE_EXTENSION = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new RateLimitExceededExceptionMapper()) .addProvider(new RateLimitExceededExceptionMapper())
.setMapper(SystemMapper.jsonMapper()) .setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())

View File

@ -13,8 +13,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableSet; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import java.security.MessageDigest; import java.security.MessageDigest;
@ -36,7 +35,6 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.entities.UserRemoteConfig; import org.whispersystems.textsecuregcm.entities.UserRemoteConfig;
import org.whispersystems.textsecuregcm.entities.UserRemoteConfigList; import org.whispersystems.textsecuregcm.entities.UserRemoteConfigList;
import org.whispersystems.textsecuregcm.mappers.DeviceLimitExceededExceptionMapper; import org.whispersystems.textsecuregcm.mappers.DeviceLimitExceededExceptionMapper;
@ -56,8 +54,7 @@ class RemoteConfigControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder() private static final ResourceExtension resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addProvider(new DeviceLimitExceededExceptionMapper()) .addProvider(new DeviceLimitExceededExceptionMapper())
.addResource(new RemoteConfigController(remoteConfigsManager, Map.of("maxGroupSize", "42"), TEST_CLOCK)) .addResource(new RemoteConfigController(remoteConfigsManager, Map.of("maxGroupSize", "42"), TEST_CLOCK))
@ -67,7 +64,7 @@ class RemoteConfigControllerTest {
@BeforeEach @BeforeEach
void setup() throws Exception { void setup() throws Exception {
when(remoteConfigsManager.getAll()).thenReturn(new LinkedList<>() {{ when(remoteConfigsManager.getAll()).thenReturn(new LinkedList<>() {{
add(new RemoteConfig("android.stickers", 25, Set.of(AuthHelper.DISABLED_UUID, AuthHelper.INVALID_UUID), null, add(new RemoteConfig("android.stickers", 25, Set.of(AuthHelper.VALID_UUID_3, AuthHelper.INVALID_UUID), null,
null, null)); null, null));
add(new RemoteConfig("ios.stickers", 50, Set.of(), null, null, null)); add(new RemoteConfig("ios.stickers", 50, Set.of(), null, null, null));
add(new RemoteConfig("always.true", 100, Set.of(), null, null, null)); add(new RemoteConfig("always.true", 100, Set.of(), null, null, null));

View File

@ -9,8 +9,7 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes; import static org.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
import com.google.common.collect.ImmutableSet; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
@ -18,7 +17,6 @@ import org.glassfish.jersey.test.grizzly.GrizzlyWebTestContainerFactory;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator; import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration; import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
@ -38,8 +36,7 @@ class SecureStorageControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder() private static final ResourceExtension resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.setMapper(SystemMapper.jsonMapper()) .setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new SecureStorageController(STORAGE_CREDENTIAL_GENERATOR)) .addResource(new SecureStorageController(STORAGE_CREDENTIAL_GENERATOR))

View File

@ -11,8 +11,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableSet; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import java.util.Base64; import java.util.Base64;
@ -22,7 +21,6 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.entities.StickerPackFormUploadAttributes; import org.whispersystems.textsecuregcm.entities.StickerPackFormUploadAttributes;
import org.whispersystems.textsecuregcm.limits.RateLimiter; import org.whispersystems.textsecuregcm.limits.RateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiters; import org.whispersystems.textsecuregcm.limits.RateLimiters;
@ -37,8 +35,7 @@ class StickerControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder() private static final ResourceExtension resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.setMapper(SystemMapper.jsonMapper()) .setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new StickerController(rateLimiters, "foo", "bar", "us-east-1", "mybucket")) .addResource(new StickerController(rateLimiters, "foo", "bar", "us-east-1", "mybucket"))

View File

@ -22,7 +22,7 @@ import static org.whispersystems.textsecuregcm.util.AttributeValues.s;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.stripe.exception.ApiException; import com.stripe.exception.ApiException;
import com.stripe.model.PaymentIntent; import com.stripe.model.PaymentIntent;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport; import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension; import io.dropwizard.testing.junit5.ResourceExtension;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -56,7 +56,6 @@ import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import org.signal.libsignal.zkgroup.receipts.ServerZkReceiptOperations; import org.signal.libsignal.zkgroup.receipts.ServerZkReceiptOperations;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.badges.BadgeTranslator; import org.whispersystems.textsecuregcm.badges.BadgeTranslator;
import org.whispersystems.textsecuregcm.badges.LevelTranslator; import org.whispersystems.textsecuregcm.badges.LevelTranslator;
import org.whispersystems.textsecuregcm.configuration.OneTimeDonationConfiguration; import org.whispersystems.textsecuregcm.configuration.OneTimeDonationConfiguration;
@ -111,8 +110,7 @@ class SubscriptionControllerTest {
.addProvider(AuthHelper.getAuthFilter()) .addProvider(AuthHelper.getAuthFilter())
.addProvider(CompletionExceptionMapper.class) .addProvider(CompletionExceptionMapper.class)
.addProvider(SubscriptionProcessorExceptionMapper.class) .addProvider(SubscriptionProcessorExceptionMapper.class)
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(Set.of( .addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.setMapper(SystemMapper.jsonMapper()) .setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory()) .setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(SUBSCRIPTION_CONTROLLER) .addResource(SUBSCRIPTION_CONTROLLER)

View File

@ -59,7 +59,6 @@ class RemoteConfigsTest {
void testUpdate() { void testUpdate() {
remoteConfigs.set(new RemoteConfig("android.stickers", 50, Set.of(), "FALSE", "TRUE", null)); remoteConfigs.set(new RemoteConfig("android.stickers", 50, Set.of(), "FALSE", "TRUE", null));
remoteConfigs.set(new RemoteConfig("value.sometimes", 22, Set.of(), "def", "!", null)); remoteConfigs.set(new RemoteConfig("value.sometimes", 22, Set.of(), "def", "!", null));
remoteConfigs.set(new RemoteConfig("ios.stickers", 50, Set.of(AuthHelper.DISABLED_UUID), "FALSE", "TRUE", null));
remoteConfigs.set(new RemoteConfig("ios.stickers", 75, Set.of(), "FALSE", "TRUE", null)); remoteConfigs.set(new RemoteConfig("ios.stickers", 75, Set.of(), "FALSE", "TRUE", null));
remoteConfigs.set(new RemoteConfig("value.sometimes", 77, Set.of(), "hey", "wut", null)); remoteConfigs.set(new RemoteConfig("value.sometimes", 77, Set.of(), "hey", "wut", null));

View File

@ -32,8 +32,6 @@ import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.ecc.ECPublicKey; import org.signal.libsignal.protocol.ecc.ECPublicKey;
import org.whispersystems.textsecuregcm.auth.AccountAuthenticator; import org.whispersystems.textsecuregcm.auth.AccountAuthenticator;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount; import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccountAuthenticator;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash; import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier; import org.whispersystems.textsecuregcm.identity.AciServiceIdentifier;
import org.whispersystems.textsecuregcm.identity.IdentityType; import org.whispersystems.textsecuregcm.identity.IdentityType;
@ -68,10 +66,6 @@ public class AuthHelper {
public static final UUID INVALID_UUID = UUID.randomUUID(); public static final UUID INVALID_UUID = UUID.randomUUID();
public static final String INVALID_PASSWORD = "bar"; public static final String INVALID_PASSWORD = "bar";
public static final String DISABLED_NUMBER = "+78888888";
public static final UUID DISABLED_UUID = UUID.randomUUID();
public static final String DISABLED_PASSWORD = "poof";
public static final String UNDISCOVERABLE_NUMBER = "+18005551234"; public static final String UNDISCOVERABLE_NUMBER = "+18005551234";
public static final UUID UNDISCOVERABLE_UUID = UUID.randomUUID(); public static final UUID UNDISCOVERABLE_UUID = UUID.randomUUID();
public static final String UNDISCOVERABLE_PASSWORD = "IT'S A SECRET TO EVERYBODY."; public static final String UNDISCOVERABLE_PASSWORD = "IT'S A SECRET TO EVERYBODY.";
@ -82,13 +76,11 @@ public class AuthHelper {
public static AccountsManager ACCOUNTS_MANAGER = mock(AccountsManager.class); public static AccountsManager ACCOUNTS_MANAGER = mock(AccountsManager.class);
public static Account VALID_ACCOUNT = mock(Account.class ); public static Account VALID_ACCOUNT = mock(Account.class );
public static Account VALID_ACCOUNT_TWO = mock(Account.class ); public static Account VALID_ACCOUNT_TWO = mock(Account.class );
public static Account DISABLED_ACCOUNT = mock(Account.class );
public static Account UNDISCOVERABLE_ACCOUNT = mock(Account.class ); public static Account UNDISCOVERABLE_ACCOUNT = mock(Account.class );
public static Account VALID_ACCOUNT_3 = mock(Account.class ); public static Account VALID_ACCOUNT_3 = mock(Account.class );
public static Device VALID_DEVICE = mock(Device.class); public static Device VALID_DEVICE = mock(Device.class);
public static Device VALID_DEVICE_TWO = mock(Device.class); public static Device VALID_DEVICE_TWO = mock(Device.class);
public static Device DISABLED_DEVICE = mock(Device.class);
public static Device UNDISCOVERABLE_DEVICE = mock(Device.class); public static Device UNDISCOVERABLE_DEVICE = mock(Device.class);
public static Device VALID_DEVICE_3_PRIMARY = mock(Device.class); public static Device VALID_DEVICE_3_PRIMARY = mock(Device.class);
public static Device VALID_DEVICE_3_LINKED = mock(Device.class); public static Device VALID_DEVICE_3_LINKED = mock(Device.class);
@ -97,7 +89,6 @@ public class AuthHelper {
private static SaltedTokenHash VALID_CREDENTIALS_TWO = mock(SaltedTokenHash.class); private static SaltedTokenHash VALID_CREDENTIALS_TWO = mock(SaltedTokenHash.class);
private static SaltedTokenHash VALID_CREDENTIALS_3_PRIMARY = mock(SaltedTokenHash.class); private static SaltedTokenHash VALID_CREDENTIALS_3_PRIMARY = mock(SaltedTokenHash.class);
private static SaltedTokenHash VALID_CREDENTIALS_3_LINKED = mock(SaltedTokenHash.class); private static SaltedTokenHash VALID_CREDENTIALS_3_LINKED = mock(SaltedTokenHash.class);
private static SaltedTokenHash DISABLED_CREDENTIALS = mock(SaltedTokenHash.class);
private static SaltedTokenHash UNDISCOVERABLE_CREDENTIALS = mock(SaltedTokenHash.class); private static SaltedTokenHash UNDISCOVERABLE_CREDENTIALS = mock(SaltedTokenHash.class);
private static final Collection<TestAccount> EXTENSION_TEST_ACCOUNTS = new HashSet<>(); private static final Collection<TestAccount> EXTENSION_TEST_ACCOUNTS = new HashSet<>();
@ -107,33 +98,28 @@ public class AuthHelper {
when(VALID_CREDENTIALS_TWO.verify("baz")).thenReturn(true); when(VALID_CREDENTIALS_TWO.verify("baz")).thenReturn(true);
when(VALID_CREDENTIALS_3_PRIMARY.verify(VALID_PASSWORD_3_PRIMARY)).thenReturn(true); when(VALID_CREDENTIALS_3_PRIMARY.verify(VALID_PASSWORD_3_PRIMARY)).thenReturn(true);
when(VALID_CREDENTIALS_3_LINKED.verify(VALID_PASSWORD_3_LINKED)).thenReturn(true); when(VALID_CREDENTIALS_3_LINKED.verify(VALID_PASSWORD_3_LINKED)).thenReturn(true);
when(DISABLED_CREDENTIALS.verify(DISABLED_PASSWORD)).thenReturn(true);
when(UNDISCOVERABLE_CREDENTIALS.verify(UNDISCOVERABLE_PASSWORD)).thenReturn(true); when(UNDISCOVERABLE_CREDENTIALS.verify(UNDISCOVERABLE_PASSWORD)).thenReturn(true);
when(VALID_DEVICE.getAuthTokenHash()).thenReturn(VALID_CREDENTIALS); when(VALID_DEVICE.getAuthTokenHash()).thenReturn(VALID_CREDENTIALS);
when(VALID_DEVICE_TWO.getAuthTokenHash()).thenReturn(VALID_CREDENTIALS_TWO); when(VALID_DEVICE_TWO.getAuthTokenHash()).thenReturn(VALID_CREDENTIALS_TWO);
when(VALID_DEVICE_3_PRIMARY.getAuthTokenHash()).thenReturn(VALID_CREDENTIALS_3_PRIMARY); when(VALID_DEVICE_3_PRIMARY.getAuthTokenHash()).thenReturn(VALID_CREDENTIALS_3_PRIMARY);
when(VALID_DEVICE_3_LINKED.getAuthTokenHash()).thenReturn(VALID_CREDENTIALS_3_LINKED); when(VALID_DEVICE_3_LINKED.getAuthTokenHash()).thenReturn(VALID_CREDENTIALS_3_LINKED);
when(DISABLED_DEVICE.getAuthTokenHash()).thenReturn(DISABLED_CREDENTIALS);
when(UNDISCOVERABLE_DEVICE.getAuthTokenHash()).thenReturn(UNDISCOVERABLE_CREDENTIALS); when(UNDISCOVERABLE_DEVICE.getAuthTokenHash()).thenReturn(UNDISCOVERABLE_CREDENTIALS);
when(VALID_DEVICE.isPrimary()).thenReturn(true); when(VALID_DEVICE.isPrimary()).thenReturn(true);
when(VALID_DEVICE_TWO.isPrimary()).thenReturn(true); when(VALID_DEVICE_TWO.isPrimary()).thenReturn(true);
when(DISABLED_DEVICE.isPrimary()).thenReturn(true);
when(UNDISCOVERABLE_DEVICE.isPrimary()).thenReturn(true); when(UNDISCOVERABLE_DEVICE.isPrimary()).thenReturn(true);
when(VALID_DEVICE_3_PRIMARY.isPrimary()).thenReturn(true); when(VALID_DEVICE_3_PRIMARY.isPrimary()).thenReturn(true);
when(VALID_DEVICE_3_LINKED.isPrimary()).thenReturn(false); when(VALID_DEVICE_3_LINKED.isPrimary()).thenReturn(false);
when(VALID_DEVICE.getId()).thenReturn(Device.PRIMARY_ID); when(VALID_DEVICE.getId()).thenReturn(Device.PRIMARY_ID);
when(VALID_DEVICE_TWO.getId()).thenReturn(Device.PRIMARY_ID); when(VALID_DEVICE_TWO.getId()).thenReturn(Device.PRIMARY_ID);
when(DISABLED_DEVICE.getId()).thenReturn(Device.PRIMARY_ID);
when(UNDISCOVERABLE_DEVICE.getId()).thenReturn(Device.PRIMARY_ID); when(UNDISCOVERABLE_DEVICE.getId()).thenReturn(Device.PRIMARY_ID);
when(VALID_DEVICE_3_PRIMARY.getId()).thenReturn(Device.PRIMARY_ID); when(VALID_DEVICE_3_PRIMARY.getId()).thenReturn(Device.PRIMARY_ID);
when(VALID_DEVICE_3_LINKED.getId()).thenReturn((byte) 2); when(VALID_DEVICE_3_LINKED.getId()).thenReturn((byte) 2);
when(VALID_DEVICE.isEnabled()).thenReturn(true); when(VALID_DEVICE.isEnabled()).thenReturn(true);
when(VALID_DEVICE_TWO.isEnabled()).thenReturn(true); when(VALID_DEVICE_TWO.isEnabled()).thenReturn(true);
when(DISABLED_DEVICE.isEnabled()).thenReturn(false);
when(UNDISCOVERABLE_DEVICE.isPrimary()).thenReturn(true); when(UNDISCOVERABLE_DEVICE.isPrimary()).thenReturn(true);
when(VALID_DEVICE_3_PRIMARY.isEnabled()).thenReturn(true); when(VALID_DEVICE_3_PRIMARY.isEnabled()).thenReturn(true);
when(VALID_DEVICE_3_LINKED.isEnabled()).thenReturn(true); when(VALID_DEVICE_3_LINKED.isEnabled()).thenReturn(true);
@ -142,8 +128,6 @@ public class AuthHelper {
when(VALID_ACCOUNT.getPrimaryDevice()).thenReturn(VALID_DEVICE); when(VALID_ACCOUNT.getPrimaryDevice()).thenReturn(VALID_DEVICE);
when(VALID_ACCOUNT_TWO.getDevice(eq(Device.PRIMARY_ID))).thenReturn(Optional.of(VALID_DEVICE_TWO)); when(VALID_ACCOUNT_TWO.getDevice(eq(Device.PRIMARY_ID))).thenReturn(Optional.of(VALID_DEVICE_TWO));
when(VALID_ACCOUNT_TWO.getPrimaryDevice()).thenReturn(VALID_DEVICE_TWO); when(VALID_ACCOUNT_TWO.getPrimaryDevice()).thenReturn(VALID_DEVICE_TWO);
when(DISABLED_ACCOUNT.getDevice(eq(Device.PRIMARY_ID))).thenReturn(Optional.of(DISABLED_DEVICE));
when(DISABLED_ACCOUNT.getPrimaryDevice()).thenReturn(DISABLED_DEVICE);
when(UNDISCOVERABLE_ACCOUNT.getDevice(eq(Device.PRIMARY_ID))).thenReturn(Optional.of(UNDISCOVERABLE_DEVICE)); when(UNDISCOVERABLE_ACCOUNT.getDevice(eq(Device.PRIMARY_ID))).thenReturn(Optional.of(UNDISCOVERABLE_DEVICE));
when(UNDISCOVERABLE_ACCOUNT.getPrimaryDevice()).thenReturn(UNDISCOVERABLE_DEVICE); when(UNDISCOVERABLE_ACCOUNT.getPrimaryDevice()).thenReturn(UNDISCOVERABLE_DEVICE);
when(VALID_ACCOUNT_3.getDevice(Device.PRIMARY_ID)).thenReturn(Optional.of(VALID_DEVICE_3_PRIMARY)); when(VALID_ACCOUNT_3.getDevice(Device.PRIMARY_ID)).thenReturn(Optional.of(VALID_DEVICE_3_PRIMARY));
@ -152,7 +136,6 @@ public class AuthHelper {
when(VALID_ACCOUNT.getDevices()).thenReturn(List.of(VALID_DEVICE)); when(VALID_ACCOUNT.getDevices()).thenReturn(List.of(VALID_DEVICE));
when(VALID_ACCOUNT_TWO.getDevices()).thenReturn(List.of(VALID_DEVICE_TWO)); when(VALID_ACCOUNT_TWO.getDevices()).thenReturn(List.of(VALID_DEVICE_TWO));
when(DISABLED_ACCOUNT.getDevices()).thenReturn(List.of(DISABLED_DEVICE));
when(UNDISCOVERABLE_ACCOUNT.getDevices()).thenReturn(List.of(UNDISCOVERABLE_DEVICE)); when(UNDISCOVERABLE_ACCOUNT.getDevices()).thenReturn(List.of(UNDISCOVERABLE_DEVICE));
when(VALID_ACCOUNT_3.getDevices()).thenReturn(List.of(VALID_DEVICE_3_PRIMARY, VALID_DEVICE_3_LINKED)); when(VALID_ACCOUNT_3.getDevices()).thenReturn(List.of(VALID_DEVICE_3_PRIMARY, VALID_DEVICE_3_LINKED));
@ -168,9 +151,6 @@ public class AuthHelper {
when(VALID_ACCOUNT_TWO.getPhoneNumberIdentifier()).thenReturn(VALID_PNI_TWO); when(VALID_ACCOUNT_TWO.getPhoneNumberIdentifier()).thenReturn(VALID_PNI_TWO);
when(VALID_ACCOUNT_TWO.getIdentifier(IdentityType.ACI)).thenReturn(VALID_UUID_TWO); when(VALID_ACCOUNT_TWO.getIdentifier(IdentityType.ACI)).thenReturn(VALID_UUID_TWO);
when(VALID_ACCOUNT_TWO.getPhoneNumberIdentifier()).thenReturn(VALID_PNI_TWO); when(VALID_ACCOUNT_TWO.getPhoneNumberIdentifier()).thenReturn(VALID_PNI_TWO);
when(DISABLED_ACCOUNT.getNumber()).thenReturn(DISABLED_NUMBER);
when(DISABLED_ACCOUNT.getUuid()).thenReturn(DISABLED_UUID);
when(DISABLED_ACCOUNT.getIdentifier(IdentityType.ACI)).thenReturn(DISABLED_UUID);
when(UNDISCOVERABLE_ACCOUNT.getNumber()).thenReturn(UNDISCOVERABLE_NUMBER); when(UNDISCOVERABLE_ACCOUNT.getNumber()).thenReturn(UNDISCOVERABLE_NUMBER);
when(UNDISCOVERABLE_ACCOUNT.getUuid()).thenReturn(UNDISCOVERABLE_UUID); when(UNDISCOVERABLE_ACCOUNT.getUuid()).thenReturn(UNDISCOVERABLE_UUID);
when(UNDISCOVERABLE_ACCOUNT.getIdentifier(IdentityType.ACI)).thenReturn(UNDISCOVERABLE_UUID); when(UNDISCOVERABLE_ACCOUNT.getIdentifier(IdentityType.ACI)).thenReturn(UNDISCOVERABLE_UUID);
@ -182,13 +162,11 @@ public class AuthHelper {
when(VALID_ACCOUNT.isEnabled()).thenReturn(true); when(VALID_ACCOUNT.isEnabled()).thenReturn(true);
when(VALID_ACCOUNT_TWO.isEnabled()).thenReturn(true); when(VALID_ACCOUNT_TWO.isEnabled()).thenReturn(true);
when(DISABLED_ACCOUNT.isEnabled()).thenReturn(false);
when(UNDISCOVERABLE_ACCOUNT.isEnabled()).thenReturn(true); when(UNDISCOVERABLE_ACCOUNT.isEnabled()).thenReturn(true);
when(VALID_ACCOUNT_3.isEnabled()).thenReturn(true); when(VALID_ACCOUNT_3.isEnabled()).thenReturn(true);
when(VALID_ACCOUNT.isDiscoverableByPhoneNumber()).thenReturn(true); when(VALID_ACCOUNT.isDiscoverableByPhoneNumber()).thenReturn(true);
when(VALID_ACCOUNT_TWO.isDiscoverableByPhoneNumber()).thenReturn(true); when(VALID_ACCOUNT_TWO.isDiscoverableByPhoneNumber()).thenReturn(true);
when(DISABLED_ACCOUNT.isDiscoverableByPhoneNumber()).thenReturn(true);
when(UNDISCOVERABLE_ACCOUNT.isDiscoverableByPhoneNumber()).thenReturn(false); when(UNDISCOVERABLE_ACCOUNT.isDiscoverableByPhoneNumber()).thenReturn(false);
when(VALID_ACCOUNT_3.isDiscoverableByPhoneNumber()).thenReturn(true); when(VALID_ACCOUNT_3.isDiscoverableByPhoneNumber()).thenReturn(true);
@ -196,7 +174,6 @@ public class AuthHelper {
when(VALID_ACCOUNT.isIdentifiedBy(new PniServiceIdentifier(VALID_PNI))).thenReturn(true); when(VALID_ACCOUNT.isIdentifiedBy(new PniServiceIdentifier(VALID_PNI))).thenReturn(true);
when(VALID_ACCOUNT_TWO.isIdentifiedBy(new AciServiceIdentifier(VALID_UUID_TWO))).thenReturn(true); when(VALID_ACCOUNT_TWO.isIdentifiedBy(new AciServiceIdentifier(VALID_UUID_TWO))).thenReturn(true);
when(VALID_ACCOUNT_TWO.isIdentifiedBy(new PniServiceIdentifier(VALID_PNI_TWO))).thenReturn(true); when(VALID_ACCOUNT_TWO.isIdentifiedBy(new PniServiceIdentifier(VALID_PNI_TWO))).thenReturn(true);
when(DISABLED_ACCOUNT.isIdentifiedBy(new AciServiceIdentifier(DISABLED_UUID))).thenReturn(true);
when(UNDISCOVERABLE_ACCOUNT.isIdentifiedBy(new AciServiceIdentifier(UNDISCOVERABLE_UUID))).thenReturn(true); when(UNDISCOVERABLE_ACCOUNT.isIdentifiedBy(new AciServiceIdentifier(UNDISCOVERABLE_UUID))).thenReturn(true);
when(VALID_ACCOUNT_3.isIdentifiedBy(new AciServiceIdentifier(VALID_UUID_3))).thenReturn(true); when(VALID_ACCOUNT_3.isIdentifiedBy(new AciServiceIdentifier(VALID_UUID_3))).thenReturn(true);
when(VALID_ACCOUNT_3.isIdentifiedBy(new PniServiceIdentifier(VALID_PNI_3))).thenReturn(true); when(VALID_ACCOUNT_3.isIdentifiedBy(new PniServiceIdentifier(VALID_PNI_3))).thenReturn(true);
@ -213,9 +190,6 @@ public class AuthHelper {
when(ACCOUNTS_MANAGER.getByAccountIdentifier(VALID_UUID_TWO)).thenReturn(Optional.of(VALID_ACCOUNT_TWO)); when(ACCOUNTS_MANAGER.getByAccountIdentifier(VALID_UUID_TWO)).thenReturn(Optional.of(VALID_ACCOUNT_TWO));
when(ACCOUNTS_MANAGER.getByPhoneNumberIdentifier(VALID_PNI_TWO)).thenReturn(Optional.of(VALID_ACCOUNT_TWO)); when(ACCOUNTS_MANAGER.getByPhoneNumberIdentifier(VALID_PNI_TWO)).thenReturn(Optional.of(VALID_ACCOUNT_TWO));
when(ACCOUNTS_MANAGER.getByE164(DISABLED_NUMBER)).thenReturn(Optional.of(DISABLED_ACCOUNT));
when(ACCOUNTS_MANAGER.getByAccountIdentifier(DISABLED_UUID)).thenReturn(Optional.of(DISABLED_ACCOUNT));
when(ACCOUNTS_MANAGER.getByE164(UNDISCOVERABLE_NUMBER)).thenReturn(Optional.of(UNDISCOVERABLE_ACCOUNT)); when(ACCOUNTS_MANAGER.getByE164(UNDISCOVERABLE_NUMBER)).thenReturn(Optional.of(UNDISCOVERABLE_ACCOUNT));
when(ACCOUNTS_MANAGER.getByAccountIdentifier(UNDISCOVERABLE_UUID)).thenReturn(Optional.of(UNDISCOVERABLE_ACCOUNT)); when(ACCOUNTS_MANAGER.getByAccountIdentifier(UNDISCOVERABLE_UUID)).thenReturn(Optional.of(UNDISCOVERABLE_ACCOUNT));
@ -231,11 +205,8 @@ public class AuthHelper {
AuthFilter<BasicCredentials, AuthenticatedAccount> accountAuthFilter = new BasicCredentialAuthFilter.Builder<AuthenticatedAccount>().setAuthenticator( AuthFilter<BasicCredentials, AuthenticatedAccount> accountAuthFilter = new BasicCredentialAuthFilter.Builder<AuthenticatedAccount>().setAuthenticator(
new AccountAuthenticator(ACCOUNTS_MANAGER)).buildAuthFilter(); new AccountAuthenticator(ACCOUNTS_MANAGER)).buildAuthFilter();
AuthFilter<BasicCredentials, DisabledPermittedAuthenticatedAccount> disabledPermittedAccountAuthFilter = new BasicCredentialAuthFilter.Builder<DisabledPermittedAuthenticatedAccount>().setAuthenticator(
new DisabledPermittedAccountAuthenticator(ACCOUNTS_MANAGER)).buildAuthFilter();
return new PolymorphicAuthDynamicFeature<>(ImmutableMap.of(AuthenticatedAccount.class, accountAuthFilter, return new PolymorphicAuthDynamicFeature<>(ImmutableMap.of(AuthenticatedAccount.class, accountAuthFilter));
DisabledPermittedAuthenticatedAccount.class, disabledPermittedAccountAuthFilter));
} }
public static String getAuthHeader(UUID uuid, byte deviceId, String password) { public static String getAuthHeader(UUID uuid, byte deviceId, String password) {
@ -336,9 +307,7 @@ public class AuthHelper {
@Override @Override
public void afterEach(final ExtensionContext context) { public void afterEach(final ExtensionContext context) {
EXTENSION_TEST_ACCOUNTS.forEach(testAccount -> { EXTENSION_TEST_ACCOUNTS.forEach(testAccount -> testAccount.teardown(ACCOUNTS_MANAGER));
testAccount.teardown(ACCOUNTS_MANAGER);
});
EXTENSION_TEST_ACCOUNTS.clear(); EXTENSION_TEST_ACCOUNTS.clear();
} }

@ -1 +1 @@
Subproject commit 5f4622c184dee3eebb0ec7a909f0992e32596567 Subproject commit fd935511ac7bacec9465edcb4fa2f844a48df759