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 javax.ws.rs.Consumes;
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
@ -42,10 +41,6 @@ public class OpenApiExtension extends AbstractOpenAPIExtension {
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
* 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)) {
return AUTHENTICATED_ACCOUNT;
}
if (type instanceof SimpleType simpleType
&& simpleType.getRawClass().equals(DisabledPermittedAuthenticatedAccount.class)) {
return DISABLED_PERMITTED_AUTHENTICATED_ACCOUNT;
}
if (type instanceof SimpleType simpleType
&& isOptionalOfType(simpleType, AuthenticatedAccount.class)) {
return OPTIONAL_AUTHENTICATED_ACCOUNT;
}
if (type instanceof SimpleType simpleType
&& isOptionalOfType(simpleType, DisabledPermittedAuthenticatedAccount.class)) {
return OPTIONAL_DISABLED_PERMITTED_AUTHENTICATED_ACCOUNT;
}
}
return super.extractParameters(

View File

@ -7,9 +7,7 @@ package org.signal.openapi;
import static com.google.common.base.MoreObjects.firstNonNull;
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_DISABLED_PERMITTED_AUTHENTICATED_ACCOUNT;
import com.fasterxml.jackson.annotation.JsonView;
import com.google.common.collect.ImmutableList;
@ -54,13 +52,13 @@ public class OpenApiReader extends Reader {
final ResolvedParameter resolved = super.getParameters(
type, annotations, operation, classConsumes, methodConsumes, jsonViewAnnotation);
if (resolved == AUTHENTICATED_ACCOUNT || resolved == DISABLED_PERMITTED_AUTHENTICATED_ACCOUNT) {
if (resolved == AUTHENTICATED_ACCOUNT) {
operation.setSecurity(ImmutableList.<SecurityRequirement>builder()
.addAll(firstNonNull(operation.getSecurity(), Collections.emptyList()))
.add(new SecurityRequirement().addList(AUTHENTICATED_ACCOUNT_AUTH_SCHEMA))
.build());
}
if (resolved == OPTIONAL_AUTHENTICATED_ACCOUNT || resolved == OPTIONAL_DISABLED_PERMITTED_AUTHENTICATED_ACCOUNT) {
if (resolved == OPTIONAL_AUTHENTICATED_ACCOUNT) {
operation.setSecurity(ImmutableList.<SecurityRequirement>builder()
.addAll(firstNonNull(operation.getSecurity(), Collections.emptyList()))
.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 java.util.Objects.requireNonNull;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthFilter;
import io.dropwizard.auth.PolymorphicAuthDynamicFeature;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.auth.basic.BasicCredentials;
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.auth.AccountAuthenticator;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.BaseAccountAuthenticator;
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.PhoneVerificationTokenManager;
import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager;
@ -580,8 +575,6 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
reportMessageManager.addListener(reportedMessageMetricsListener);
final AccountAuthenticator accountAuthenticator = new AccountAuthenticator(accountsManager);
final DisabledPermittedAccountAuthenticator disabledPermittedAccountAuthenticator = new DisabledPermittedAccountAuthenticator(
accountsManager);
final MessageSender messageSender = new MessageSender(clientPresenceManager, messagesManager,
pushNotificationManager,
@ -686,13 +679,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
config.getClientCdn().getAttachmentUrls(),
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 =
new BasicCredentialAuthenticationInterceptor(new BaseAccountAuthenticator(accountsManager));
new BasicCredentialAuthenticationInterceptor(new AccountAuthenticator(accountsManager));
final ServerBuilder<?> grpcServer = ServerBuilder.forPort(config.getGrpcPort())
.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()));
final AuthFilter<BasicCredentials, AuthenticatedAccount> accountAuthFilter =
new BasicCredentialAuthFilter.Builder<AuthenticatedAccount>()
.setAuthenticator(accountAuthenticator)
.buildAuthFilter();
environment.jersey().register(new RequestStatisticsFilter(TrafficSource.HTTP));
environment.jersey().register(MultiRecipientMessageProvider.class);
environment.jersey().register(new MetricsApplicationEventListener(TrafficSource.HTTP, clientReleaseManager));
environment.jersey()
.register(new PolymorphicAuthDynamicFeature<>(ImmutableMap.of(AuthenticatedAccount.class, accountAuthFilter,
DisabledPermittedAuthenticatedAccount.class, disabledPermittedAccountAuthFilter)));
environment.jersey().register(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)));
environment.jersey().register(new AuthDynamicFeature(accountAuthFilter));
environment.jersey().register(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class));
environment.jersey().register(new WebsocketRefreshApplicationEventListener(accountsManager, clientPresenceManager));
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 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;
ReportSpamTokenProvider reportSpamTokenProvider = null;
@ -804,6 +786,8 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
}
final List<Object> commonControllers = Lists.newArrayList(
new AccountController(accountsManager, rateLimiters, turnTokenGenerator, registrationRecoveryPasswordsManager,
usernameHashZkProofVerifier),
new AccountControllerV2(accountsManager, changeNumberManager, phoneVerificationTokenManager,
registrationLockVerificationManager, rateLimiters),
new ArtController(rateLimiters, artCredentialsGenerator),
@ -824,6 +808,7 @@ public class WhisperServerService extends Application<WhisperServerConfiguration
new DirectoryV2Controller(directoryV2CredentialsGenerator),
new DonationController(clock, zkReceiptOperations, redeemedReceiptsManager, accountsManager, config.getBadges(),
ReceiptCredentialPresentation::new),
new KeysController(rateLimiters, keysManager, accountsManager),
new MessageController(rateLimiters, messageByteLimitCardinalityEstimator, messageSender, receiptSender,
accountsManager, messagesManager, pushNotificationManager, reportMessageManager,
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
*/
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.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 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.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
Authenticator<BasicCredentials, AuthenticatedAccount> {
public class AccountAuthenticator implements 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) {
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
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.
*
* @see AuthenticatedAccount
* @see DisabledPermittedAuthenticatedAccount
*/
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 Pair<String, Byte> identifierAndDeviceId =
BaseAccountAuthenticator.getIdentifierAndDeviceId(usernameComponent);
AccountAuthenticator.getIdentifierAndDeviceId(usernameComponent);
username = identifierAndDeviceId.first();
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 org.apache.commons.lang3.StringUtils;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.BaseAccountAuthenticator;
import org.whispersystems.textsecuregcm.auth.AccountAuthenticator;
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.
*
* @see AuthenticationUtil
* @see BaseAccountAuthenticator
* @see AccountAuthenticator
*/
public class BasicCredentialAuthenticationInterceptor implements ServerInterceptor {
private final BaseAccountAuthenticator baseAccountAuthenticator;
private final AccountAuthenticator accountAuthenticator;
@VisibleForTesting
static final Metadata.Key<String> BASIC_CREDENTIALS =
@ -44,8 +44,8 @@ public class BasicCredentialAuthenticationInterceptor implements ServerIntercept
private static final Metadata EMPTY_TRAILERS = new Metadata();
public BasicCredentialAuthenticationInterceptor(final BaseAccountAuthenticator baseAccountAuthenticator) {
this.baseAccountAuthenticator = baseAccountAuthenticator;
public BasicCredentialAuthenticationInterceptor(final AccountAuthenticator accountAuthenticator) {
this.accountAuthenticator = accountAuthenticator;
}
@Override
@ -62,7 +62,7 @@ public class BasicCredentialAuthenticationInterceptor implements ServerIntercept
call.close(Status.UNAUTHENTICATED.withDescription("Could not parse credentials"), EMPTY_TRAILERS);
} else {
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
baseAccountAuthenticator.authenticate(maybeCredentials.get(), false);
accountAuthenticator.authenticate(maybeCredentials.get());
if (maybeAuthenticatedAccount.isPresent()) {
final AuthenticatedAccount authenticatedAccount = maybeAuthenticatedAccount.get();

View File

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

View File

@ -42,7 +42,6 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.auth.Anonymous;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.entities.ECSignedPreKey;
import org.whispersystems.textsecuregcm.entities.KEMSignedPreKey;
@ -114,7 +113,7 @@ public class KeysController {
@ApiResponse(responseCode = "401", description = "Account authentication check failed.")
@ApiResponse(responseCode = "403", description = "Attempt to change identity key from a non-primary device.")
@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,
@Parameter(allowEmptyValue=true)
@ -124,8 +123,8 @@ public class KeysController {
description="whether this operation applies to the account (aci) or phone-number (pni) identity")
@QueryParam("identity") @DefaultValue("aci") final IdentityType identityType) {
final Account account = disabledPermittedAuth.getAccount();
final Device device = disabledPermittedAuth.getAuthenticatedDevice();
final Account account = auth.getAccount();
final Device device = auth.getAuthenticatedDevice();
final UUID identifier = account.getIdentifier(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.TestClock;
class BaseAccountAuthenticatorTest {
class AccountAuthenticatorTest {
private final long today = 1590451200000L;
private final long yesterday = today - 86_400_000L;
@ -46,7 +46,7 @@ class BaseAccountAuthenticatorTest {
private final long currentTime = today + 68_000_000L;
private AccountsManager accountsManager;
private BaseAccountAuthenticator baseAccountAuthenticator;
private AccountAuthenticator accountAuthenticator;
private TestClock clock;
private Account acct1;
private Account acct2;
@ -56,7 +56,7 @@ class BaseAccountAuthenticatorTest {
void setup() {
accountsManager = mock(AccountsManager.class);
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
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 device2 = acct2.getDevices().stream().findFirst().get();
final Account updatedAcct1 = baseAccountAuthenticator.updateLastSeen(acct1, device1);
final Account updatedAcct2 = baseAccountAuthenticator.updateLastSeen(acct2, device2);
final Account updatedAcct1 = accountAuthenticator.updateLastSeen(acct1, device1);
final Account updatedAcct2 = accountAuthenticator.updateLastSeen(acct2, device2);
verify(accountsManager, never()).updateDeviceLastSeen(eq(acct1), any(), 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 device2 = acct2.getDevices().stream().findFirst().get();
final Account updatedAcct1 = baseAccountAuthenticator.updateLastSeen(acct1, device1);
final Account updatedAcct2 = baseAccountAuthenticator.updateLastSeen(acct2, device2);
final Account updatedAcct1 = accountAuthenticator.updateLastSeen(acct1, device1);
final Account updatedAcct2 = accountAuthenticator.updateLastSeen(acct2, device2);
verify(accountsManager, never()).updateDeviceLastSeen(eq(acct1), 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 device2 = acct2.getDevices().stream().findFirst().get();
final Account updatedAcct1 = baseAccountAuthenticator.updateLastSeen(acct1, device1);
final Account updatedAcct2 = baseAccountAuthenticator.updateLastSeen(acct2, device2);
final Account updatedAcct1 = accountAuthenticator.updateLastSeen(acct1, device1);
final Account updatedAcct2 = accountAuthenticator.updateLastSeen(acct2, device2);
verify(accountsManager).updateDeviceLastSeen(eq(acct1), eq(device1), anyLong());
verify(accountsManager).updateDeviceLastSeen(eq(acct2), eq(device2), anyLong());
@ -140,7 +140,7 @@ class BaseAccountAuthenticatorTest {
final Device device = oldAccount.getDevices().stream().findFirst().get();
baseAccountAuthenticator.updateLastSeen(oldAccount, device);
accountAuthenticator.updateLastSeen(oldAccount, device);
verify(accountsManager).updateDeviceLastSeen(eq(oldAccount), eq(device), anyLong());
@ -169,7 +169,7 @@ class BaseAccountAuthenticatorTest {
when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION);
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), password), true);
accountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), password));
assertThat(maybeAuthenticatedAccount).isPresent();
assertThat(maybeAuthenticatedAccount.get().getAccount().getUuid()).isEqualTo(uuid);
@ -199,7 +199,7 @@ class BaseAccountAuthenticatorTest {
when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION);
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid + "." + deviceId, password), true);
accountAuthenticator.authenticate(new BasicCredentials(uuid + "." + deviceId, password));
assertThat(maybeAuthenticatedAccount).isPresent();
assertThat(maybeAuthenticatedAccount.get().getAccount().getUuid()).isEqualTo(uuid);
@ -208,8 +208,7 @@ class BaseAccountAuthenticatorTest {
}
@CartesianTest
void testAuthenticateEnabledRequired(
@CartesianTest.Values(booleans = {true, false}) final boolean enabledRequired,
void testAuthenticateEnabled(
@CartesianTest.Values(booleans = {true, false}) final boolean accountEnabled,
@CartesianTest.Values(booleans = {true, false}) final boolean deviceEnabled,
@CartesianTest.Values(booleans = {true, false}) final boolean authenticatedDeviceIsPrimary) {
@ -236,18 +235,15 @@ class BaseAccountAuthenticatorTest {
if (authenticatedDeviceIsPrimary) {
identifier = uuid.toString();
} else {
identifier = uuid.toString() + BaseAccountAuthenticator.DEVICE_ID_SEPARATOR + deviceId;
identifier = uuid.toString() + AccountAuthenticator.DEVICE_ID_SEPARATOR + deviceId;
}
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
@ -272,7 +268,7 @@ class BaseAccountAuthenticatorTest {
when(credentials.getVersion()).thenReturn(SaltedTokenHash.Version.V1);
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), password), true);
accountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), password));
assertThat(maybeAuthenticatedAccount).isPresent();
assertThat(maybeAuthenticatedAccount.get().getAccount().getUuid()).isEqualTo(uuid);
@ -283,7 +279,7 @@ class BaseAccountAuthenticatorTest {
}
@Test
void testAuthenticateAccountNotFound() {
assertThat(baseAccountAuthenticator.authenticate(new BasicCredentials(UUID.randomUUID().toString(), "password"), true))
assertThat(accountAuthenticator.authenticate(new BasicCredentials(UUID.randomUUID().toString(), "password")))
.isEmpty();
}
@ -309,7 +305,7 @@ class BaseAccountAuthenticatorTest {
when(credentials.getVersion()).thenReturn(SaltedTokenHash.CURRENT_VERSION);
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid + "." + (deviceId + 1), password), true);
accountAuthenticator.authenticate(new BasicCredentials(uuid + "." + (deviceId + 1), password));
assertThat(maybeAuthenticatedAccount).isEmpty();
verify(account).getDevice((byte) (deviceId + 1));
@ -339,7 +335,7 @@ class BaseAccountAuthenticatorTest {
final String incorrectPassword = password + "incorrect";
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount =
baseAccountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), incorrectPassword), true);
accountAuthenticator.authenticate(new BasicCredentials(uuid.toString(), incorrectPassword));
assertThat(maybeAuthenticatedAccount).isEmpty();
verify(credentials).verify(incorrectPassword);
@ -349,7 +345,7 @@ class BaseAccountAuthenticatorTest {
@MethodSource
void testAuthenticateMalformedCredentials(final String username) {
final Optional<AuthenticatedAccount> maybeAuthenticatedAccount = assertDoesNotThrow(
() -> baseAccountAuthenticator.authenticate(new BasicCredentials(username, "password"), true));
() -> accountAuthenticator.authenticate(new BasicCredentials(username, "password")));
assertThat(maybeAuthenticatedAccount).isEmpty();
verify(accountsManager, never()).getByAccountIdentifier(any(UUID.class));
@ -367,7 +363,7 @@ class BaseAccountAuthenticatorTest {
@MethodSource
void testGetIdentifierAndDeviceId(final String username, final String expectedIdentifier,
final byte expectedDeviceId) {
final Pair<String, Byte> identifierAndDeviceId = BaseAccountAuthenticator.getIdentifierAndDeviceId(username);
final Pair<String, Byte> identifierAndDeviceId = AccountAuthenticator.getIdentifierAndDeviceId(username);
assertEquals(expectedIdentifier, identifierAndDeviceId.first());
assertEquals(expectedDeviceId, identifierAndDeviceId.second());
@ -389,6 +385,6 @@ class BaseAccountAuthenticatorTest {
})
void testGetIdentifierAndDeviceIdMalformed(final String malformedUsername) {
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.when;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.Auth;
import io.dropwizard.auth.PolymorphicAuthDynamicFeature;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.jersey.DropwizardResourceConfig;
import io.dropwizard.jersey.jackson.JacksonMessageBodyProvider;
@ -40,7 +38,6 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@ -97,12 +94,9 @@ class AuthEnablementRefreshRequirementProviderTest {
new TestPrincipal("test", account, authenticatedDevice));
private final ResourceExtension resources = ResourceExtension.builder()
.addProvider(
new PolymorphicAuthDynamicFeature<>(ImmutableMap.of(
TestPrincipal.class,
new BasicCredentialAuthFilter.Builder<TestPrincipal>()
.setAuthenticator(c -> principalSupplier.get()).buildAuthFilter())))
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(ImmutableSet.of(TestPrincipal.class)))
.addProvider(new AuthDynamicFeature(new BasicCredentialAuthFilter.Builder<TestPrincipal>()
.setAuthenticator(c -> principalSupplier.get()).buildAuthFilter()))
.addProvider(new AuthValueFactoryProvider.Binder<>(TestPrincipal.class))
.addProvider(applicationEventListener)
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.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.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.mock;
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.EchoServiceGrpc;
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.storage.Account;
import org.whispersystems.textsecuregcm.storage.Device;
@ -47,15 +46,15 @@ class BasicCredentialAuthenticationInterceptorTest {
private Server server;
private ManagedChannel managedChannel;
private BaseAccountAuthenticator baseAccountAuthenticator;
private AccountAuthenticator accountAuthenticator;
@BeforeEach
void setUp() throws IOException {
baseAccountAuthenticator = mock(BaseAccountAuthenticator.class);
accountAuthenticator = mock(AccountAuthenticator.class);
final BasicCredentialAuthenticationInterceptor authenticationInterceptor =
new BasicCredentialAuthenticationInterceptor(baseAccountAuthenticator);
new BasicCredentialAuthenticationInterceptor(accountAuthenticator);
final String serverName = InProcessServerBuilder.generateName();
@ -87,10 +86,10 @@ class BasicCredentialAuthenticationInterceptorTest {
final Device device = mock(Device.class);
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))));
} else {
when(baseAccountAuthenticator.authenticate(any(), anyBoolean()))
when(accountAuthenticator.authenticate(any()))
.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.when;
import com.google.common.collect.ImmutableSet;
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.ResourceExtension;
import java.security.SecureRandom;
@ -55,7 +54,6 @@ import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.ArgumentCaptor;
import org.signal.libsignal.usernames.BaseUsernameException;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.SaltedTokenHash;
import org.whispersystems.textsecuregcm.auth.StoredRegistrationLock;
import org.whispersystems.textsecuregcm.auth.TurnTokenGenerator;
@ -144,10 +142,7 @@ class AccountControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder()
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
.addProvider(AuthHelper.getAuthFilter())
.addProvider(
new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class,
DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.addProvider(new JsonMappingExceptionMapper())
.addProvider(new RateLimitExceededExceptionMapper())
.addProvider(new ImpossiblePhoneNumberExceptionMapper())
@ -231,7 +226,7 @@ class AccountControllerTest {
senderTransfer,
usernameZkProofVerifier);
clearInvocations(AuthHelper.DISABLED_DEVICE);
clearInvocations(AuthHelper.VALID_DEVICE_3_PRIMARY);
}
@Test
@ -268,31 +263,20 @@ class AccountControllerTest {
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
void testSetGcmId() {
Response response =
resources.getJerseyTest()
.target("/v1/accounts/gcm/")
.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")));
assertThat(response.getStatus()).isEqualTo(204);
verify(AuthHelper.DISABLED_DEVICE, times(1)).setGcmId(eq("z000"));
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.DISABLED_ACCOUNT), anyByte(), any());
verify(AuthHelper.VALID_DEVICE_3_PRIMARY, times(1)).setGcmId(eq("z000"));
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.VALID_ACCOUNT_3), anyByte(), any());
}
@Test
@ -301,7 +285,8 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/gcm/")
.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("{}"));
assertThat(response.getStatus()).isEqualTo(422);
@ -314,14 +299,15 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/apn/")
.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")));
assertThat(response.getStatus()).isEqualTo(204);
verify(AuthHelper.DISABLED_DEVICE, times(1)).setApnId(eq("first"));
verify(AuthHelper.DISABLED_DEVICE, times(1)).setVoipApnId(eq("second"));
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.DISABLED_ACCOUNT), anyByte(), any());
verify(AuthHelper.VALID_DEVICE_3_PRIMARY, times(1)).setApnId(eq("first"));
verify(AuthHelper.VALID_DEVICE_3_PRIMARY, times(1)).setVoipApnId(eq("second"));
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.VALID_ACCOUNT_3), anyByte(), any());
}
@Test
@ -330,49 +316,31 @@ class AccountControllerTest {
resources.getJerseyTest()
.target("/v1/accounts/apn/")
.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)));
assertThat(response.getStatus()).isEqualTo(204);
verify(AuthHelper.DISABLED_DEVICE, times(1)).setApnId(eq("first"));
verify(AuthHelper.DISABLED_DEVICE, times(1)).setVoipApnId(null);
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.DISABLED_ACCOUNT), anyByte(), any());
verify(AuthHelper.VALID_DEVICE_3_PRIMARY, times(1)).setApnId(eq("first"));
verify(AuthHelper.VALID_DEVICE_3_PRIMARY, times(1)).setVoipApnId(null);
verify(accountsManager, times(1)).updateDevice(eq(AuthHelper.VALID_ACCOUNT_3), anyByte(), any());
}
@ParameterizedTest
@MethodSource
void testWhoAmI(final String path, final boolean enabledAccount, final int expectedHttpStatusCode) {
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;
}
@ValueSource(strings = {"/v1/accounts/whoami", "/v1/accounts/me"})
void testWhoAmI(final String path) {
final Response response = resources.getJerseyTest()
.target(path)
.request()
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(aci, password))
.header(HttpHeaders.AUTHORIZATION, AuthHelper.getAuthHeader(AuthHelper.VALID_UUID, AuthHelper.VALID_PASSWORD))
.get();
assertThat(response.getStatus()).isEqualTo(expectedHttpStatusCode);
assertThat(response.getStatus()).isEqualTo(200);
if (expectedHttpStatusCode == 200) {
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)
);
assertThat(response.readEntity(AccountIdentityResponse.class).uuid()).isEqualTo(AuthHelper.VALID_UUID);
}
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.when;
import com.google.common.collect.ImmutableSet;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
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.ResourceExtension;
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.ECKeyPair;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.PhoneVerificationTokenManager;
import org.whispersystems.textsecuregcm.auth.RegistrationLockError;
import org.whispersystems.textsecuregcm.auth.RegistrationLockVerificationManager;
@ -118,10 +116,7 @@ class AccountControllerV2Test {
private final ResourceExtension resources = ResourceExtension.builder()
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
.addProvider(AuthHelper.getAuthFilter())
.addProvider(
new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class,
DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.addProvider(new RateLimitExceededExceptionMapper())
.addProvider(new ImpossiblePhoneNumberExceptionMapper())
.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.when;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension;
import io.grpc.Status;
@ -52,7 +51,6 @@ import org.signal.libsignal.zkgroup.VerificationFailedException;
import org.signal.libsignal.zkgroup.backups.BackupAuthCredentialPresentation;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.AuthenticatedBackupUser;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.backup.BackupAuthManager;
import org.whispersystems.textsecuregcm.backup.BackupAuthTestUtil;
import org.whispersystems.textsecuregcm.backup.BackupManager;
@ -76,8 +74,7 @@ public class ArchiveControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder()
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
.addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.addProvider(new CompletionExceptionMapper())
.addResource(new GrpcStatusRuntimeExceptionMapper())
.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.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension;
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.extension.ExtendWith;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.configuration.ArtServiceConfiguration;
@ -36,8 +34,7 @@ class ArtControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.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.when;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension;
import java.io.IOException;
@ -26,7 +25,6 @@ import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.core.Response;
import org.assertj.core.api.Assertions;
import org.assertj.core.api.Condition;
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.TusConfiguration;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.configuration.secrets.SecretBytes;
import org.whispersystems.textsecuregcm.entities.AttachmentDescriptorV2;
import org.whispersystems.textsecuregcm.entities.AttachmentDescriptorV3;
@ -93,15 +90,15 @@ class AttachmentControllerTest {
"signal@example.com", 1000, "/attach-here", RSA_PRIVATE_KEY_PEM);
resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new AttachmentControllerV2(RATE_LIMITERS, "accessKey", "accessSecret", "us-east-1", "attachmentv2-bucket"))
.addResource(new AttachmentControllerV3(RATE_LIMITERS, gcsAttachmentGenerator))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new AttachmentControllerV2(RATE_LIMITERS, "accessKey", "accessSecret", "us-east-1",
"attachmentv2-bucket"))
.addResource(new AttachmentControllerV3(RATE_LIMITERS, gcsAttachmentGenerator))
.addProvider(new AttachmentControllerV4(RATE_LIMITERS,
gcsAttachmentGenerator,
new TusAttachmentGenerator(new TusConfiguration( new SecretBytes(TUS_SECRET), TUS_URL)),
new TusAttachmentGenerator(new TusConfiguration(new SecretBytes(TUS_SECRET), TUS_URL)),
EXPERIMENT_MANAGER))
.build();
} catch (IOException | InvalidKeyException | InvalidKeySpecException e) {
@ -194,17 +191,6 @@ class AttachmentControllerTest {
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
void testV2Form() throws IOException {
AttachmentDescriptorV2 descriptor = resources.getJerseyTest()
@ -233,14 +219,4 @@ class AttachmentControllerTest {
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.when;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension;
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.calllinks.CreateCallLinkCredentialRequestContext;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.entities.GetCreateCallLinkCredentialsRequest;
import org.whispersystems.textsecuregcm.limits.RateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
@ -43,8 +41,7 @@ public class CallLinkControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.addProvider(new RateLimitExceededExceptionMapper())
.setMapper(SystemMapper.jsonMapper())
.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.assertTrue;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension;
import java.io.IOException;
@ -41,7 +40,6 @@ import org.signal.libsignal.zkgroup.auth.ServerZkAuthOperations;
import org.signal.libsignal.zkgroup.calllinks.CallLinkAuthCredentialResponse;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.CertificateGenerator;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.entities.DeliveryCertificate;
import org.whispersystems.textsecuregcm.entities.GroupCredentials;
@ -81,8 +79,7 @@ class CertificateControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new CertificateController(certificateGenerator, serverZkAuthOperations, genericServerSecretParams, clock))
@ -207,17 +204,6 @@ class CertificateControllerTest {
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
void testGetSingleGroupCredentialWithPniAsAci() {
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 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.ResourceExtension;
import java.io.IOException;
import java.time.Duration;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import javax.ws.rs.client.Entity;
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.provider.ValueSource;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.limits.RateLimitChallengeManager;
import org.whispersystems.textsecuregcm.mappers.RateLimitExceededExceptionMapper;
import org.whispersystems.textsecuregcm.push.NotPushRegisteredException;
@ -60,8 +58,7 @@ class ChallengeControllerTest {
private static final ResourceExtension EXTENSION = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
Set.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.addProvider(ScoreThresholdProvider.ScoreThresholdFeature.class)
.addProvider(PushChallengeConfigProvider.PushChallengeConfigFeature.class)
.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.when;
import com.google.common.collect.ImmutableSet;
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.ResourceExtension;
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.ECKeyPair;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.WebsocketRefreshApplicationEventListener;
import org.whispersystems.textsecuregcm.entities.AccountAttributes;
import org.whispersystems.textsecuregcm.entities.ApnRegistrationId;
@ -117,8 +115,7 @@ class DeviceControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder()
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
.addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addProvider(new WebsocketRefreshApplicationEventListener(accountsManager, clientPresenceManager))
.addProvider(new DeviceLimitExceededExceptionMapper())
@ -671,17 +668,6 @@ class DeviceControllerTest {
"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
void maxDevicesTest() {
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.when;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.ResourceExtension;
import java.time.Clock;
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.ServerZkReceiptOperations;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.configuration.BadgeConfiguration;
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
import org.whispersystems.textsecuregcm.entities.BadgeSvg;
@ -96,8 +94,7 @@ class DonationControllerTest {
resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new DonationController(clock, zkReceiptOperations, redeemedReceiptsManager, accountsManager,
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.JsonSerialize;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension;
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.ECKeyPair;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.entities.ECPreKey;
import org.whispersystems.textsecuregcm.entities.ECSignedPreKey;
@ -135,8 +133,7 @@ class KeysControllerTest {
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
.addProvider(AuthHelper.getAuthFilter())
.addProvider(CompletionExceptionMapper.class)
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(ImmutableSet.of(
AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new ServerRejectedExceptionMapper())
.addResource(new KeysController(rateLimiters, KEYS, accounts))
@ -340,18 +337,6 @@ class KeysControllerTest {
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
void validSingleRequestTestV2() {
PreKeyResponse result = resources.getJerseyTest()
@ -990,35 +975,4 @@ class KeysControllerTest {
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 com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.MoreExecutors;
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.ResourceExtension;
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.ECKeyPair;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessUtil;
import org.whispersystems.textsecuregcm.configuration.dynamic.DynamicConfiguration;
@ -184,8 +182,7 @@ class MessageControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder()
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
.addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.addProvider(RateLimitExceededExceptionMapper.class)
.addProvider(MultiRecipientMessageProvider.class)
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
@ -283,20 +280,6 @@ class MessageControllerTest {
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
void testSingleDeviceCurrent() throws Exception {
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.when;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension;
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.extension.ExtendWith;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.currency.CurrencyConversionManager;
@ -42,8 +40,7 @@ class PaymentsControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(new PaymentsController(currencyManager, paymentsCredentialsGenerator))
.build();
@ -90,17 +87,6 @@ class PaymentsControllerTest {
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
void testGetCurrencyConversions() {
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.when;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension;
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.ServerZkProfileOperations;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.configuration.BadgeConfiguration;
import org.whispersystems.textsecuregcm.configuration.BadgesConfiguration;
@ -149,8 +147,7 @@ class ProfileControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder()
.addProperty(ServerProperties.UNWRAP_COMPLETION_STAGE_IN_WRITER_ENABLE, Boolean.TRUE)
.addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.addProvider(new RateLimitExceededExceptionMapper())
.setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
@ -384,18 +381,6 @@ class ProfileControllerTest {
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
void testProfileCapabilities() {
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.when;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension;
import java.nio.charset.StandardCharsets;
@ -32,7 +31,6 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.ArgumentCaptor;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.entities.ProvisioningMessage;
import org.whispersystems.textsecuregcm.limits.RateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
@ -52,8 +50,7 @@ class ProvisioningControllerTest {
private static final ResourceExtension RESOURCE_EXTENSION = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.addProvider(new RateLimitExceededExceptionMapper())
.setMapper(SystemMapper.jsonMapper())
.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.when;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension;
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.extension.ExtendWith;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.entities.UserRemoteConfig;
import org.whispersystems.textsecuregcm.entities.UserRemoteConfigList;
import org.whispersystems.textsecuregcm.mappers.DeviceLimitExceededExceptionMapper;
@ -56,8 +54,7 @@ class RemoteConfigControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addProvider(new DeviceLimitExceededExceptionMapper())
.addResource(new RemoteConfigController(remoteConfigsManager, Map.of("maxGroupSize", "42"), TEST_CLOCK))
@ -67,7 +64,7 @@ class RemoteConfigControllerTest {
@BeforeEach
void setup() throws Exception {
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));
add(new RemoteConfig("ios.stickers", 50, 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.whispersystems.textsecuregcm.util.MockUtils.randomSecretBytes;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension;
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.extension.ExtendWith;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentials;
import org.whispersystems.textsecuregcm.auth.ExternalServiceCredentialsGenerator;
import org.whispersystems.textsecuregcm.configuration.SecureStorageServiceConfiguration;
@ -38,8 +36,7 @@ class SecureStorageControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.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.when;
import com.google.common.collect.ImmutableSet;
import io.dropwizard.auth.PolymorphicAuthValueFactoryProvider;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.testing.junit5.DropwizardExtensionsSupport;
import io.dropwizard.testing.junit5.ResourceExtension;
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.extension.ExtendWith;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.entities.StickerPackFormUploadAttributes;
import org.whispersystems.textsecuregcm.limits.RateLimiter;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
@ -37,8 +35,7 @@ class StickerControllerTest {
private static final ResourceExtension resources = ResourceExtension.builder()
.addProvider(AuthHelper.getAuthFilter())
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(
ImmutableSet.of(AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.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.stripe.exception.ApiException;
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.ResourceExtension;
import java.math.BigDecimal;
@ -56,7 +56,6 @@ import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.signal.libsignal.zkgroup.receipts.ServerZkReceiptOperations;
import org.whispersystems.textsecuregcm.auth.AuthenticatedAccount;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAuthenticatedAccount;
import org.whispersystems.textsecuregcm.badges.BadgeTranslator;
import org.whispersystems.textsecuregcm.badges.LevelTranslator;
import org.whispersystems.textsecuregcm.configuration.OneTimeDonationConfiguration;
@ -111,8 +110,7 @@ class SubscriptionControllerTest {
.addProvider(AuthHelper.getAuthFilter())
.addProvider(CompletionExceptionMapper.class)
.addProvider(SubscriptionProcessorExceptionMapper.class)
.addProvider(new PolymorphicAuthValueFactoryProvider.Binder<>(Set.of(
AuthenticatedAccount.class, DisabledPermittedAuthenticatedAccount.class)))
.addProvider(new AuthValueFactoryProvider.Binder<>(AuthenticatedAccount.class))
.setMapper(SystemMapper.jsonMapper())
.setTestContainerFactory(new GrizzlyWebTestContainerFactory())
.addResource(SUBSCRIPTION_CONTROLLER)

View File

@ -59,7 +59,6 @@ class RemoteConfigsTest {
void testUpdate() {
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("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("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.whispersystems.textsecuregcm.auth.AccountAuthenticator;
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.identity.AciServiceIdentifier;
import org.whispersystems.textsecuregcm.identity.IdentityType;
@ -68,10 +66,6 @@ public class AuthHelper {
public static final UUID INVALID_UUID = UUID.randomUUID();
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 UUID UNDISCOVERABLE_UUID = UUID.randomUUID();
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 Account VALID_ACCOUNT = 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 VALID_ACCOUNT_3 = mock(Account.class );
public static Device VALID_DEVICE = 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 VALID_DEVICE_3_PRIMARY = 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_3_PRIMARY = 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 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_3_PRIMARY.verify(VALID_PASSWORD_3_PRIMARY)).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(VALID_DEVICE.getAuthTokenHash()).thenReturn(VALID_CREDENTIALS);
when(VALID_DEVICE_TWO.getAuthTokenHash()).thenReturn(VALID_CREDENTIALS_TWO);
when(VALID_DEVICE_3_PRIMARY.getAuthTokenHash()).thenReturn(VALID_CREDENTIALS_3_PRIMARY);
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(VALID_DEVICE.isPrimary()).thenReturn(true);
when(VALID_DEVICE_TWO.isPrimary()).thenReturn(true);
when(DISABLED_DEVICE.isPrimary()).thenReturn(true);
when(UNDISCOVERABLE_DEVICE.isPrimary()).thenReturn(true);
when(VALID_DEVICE_3_PRIMARY.isPrimary()).thenReturn(true);
when(VALID_DEVICE_3_LINKED.isPrimary()).thenReturn(false);
when(VALID_DEVICE.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(VALID_DEVICE_3_PRIMARY.getId()).thenReturn(Device.PRIMARY_ID);
when(VALID_DEVICE_3_LINKED.getId()).thenReturn((byte) 2);
when(VALID_DEVICE.isEnabled()).thenReturn(true);
when(VALID_DEVICE_TWO.isEnabled()).thenReturn(true);
when(DISABLED_DEVICE.isEnabled()).thenReturn(false);
when(UNDISCOVERABLE_DEVICE.isPrimary()).thenReturn(true);
when(VALID_DEVICE_3_PRIMARY.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_TWO.getDevice(eq(Device.PRIMARY_ID))).thenReturn(Optional.of(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.getPrimaryDevice()).thenReturn(UNDISCOVERABLE_DEVICE);
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_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(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.getIdentifier(IdentityType.ACI)).thenReturn(VALID_UUID_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.getUuid()).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_TWO.isEnabled()).thenReturn(true);
when(DISABLED_ACCOUNT.isEnabled()).thenReturn(false);
when(UNDISCOVERABLE_ACCOUNT.isEnabled()).thenReturn(true);
when(VALID_ACCOUNT_3.isEnabled()).thenReturn(true);
when(VALID_ACCOUNT.isDiscoverableByPhoneNumber()).thenReturn(true);
when(VALID_ACCOUNT_TWO.isDiscoverableByPhoneNumber()).thenReturn(true);
when(DISABLED_ACCOUNT.isDiscoverableByPhoneNumber()).thenReturn(true);
when(UNDISCOVERABLE_ACCOUNT.isDiscoverableByPhoneNumber()).thenReturn(false);
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_TWO.isIdentifiedBy(new AciServiceIdentifier(VALID_UUID_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(VALID_ACCOUNT_3.isIdentifiedBy(new AciServiceIdentifier(VALID_UUID_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.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.getByAccountIdentifier(UNDISCOVERABLE_UUID)).thenReturn(Optional.of(UNDISCOVERABLE_ACCOUNT));
@ -231,11 +205,8 @@ public class AuthHelper {
AuthFilter<BasicCredentials, AuthenticatedAccount> accountAuthFilter = new BasicCredentialAuthFilter.Builder<AuthenticatedAccount>().setAuthenticator(
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,
DisabledPermittedAuthenticatedAccount.class, disabledPermittedAccountAuthFilter));
return new PolymorphicAuthDynamicFeature<>(ImmutableMap.of(AuthenticatedAccount.class, accountAuthFilter));
}
public static String getAuthHeader(UUID uuid, byte deviceId, String password) {
@ -336,9 +307,7 @@ public class AuthHelper {
@Override
public void afterEach(final ExtensionContext context) {
EXTENSION_TEST_ACCOUNTS.forEach(testAccount -> {
testAccount.teardown(ACCOUNTS_MANAGER);
});
EXTENSION_TEST_ACCOUNTS.forEach(testAccount -> testAccount.teardown(ACCOUNTS_MANAGER));
EXTENSION_TEST_ACCOUNTS.clear();
}

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