mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-20 13:27:59 +01:00
Stop monitoring device "enabled" state changes from auth enablement refresh requirement provider
Device enabled states no longer affect anything at an authentication level
This commit is contained in:
committed by
Jon Chambers
parent
2f76738b50
commit
2d1610b075
@@ -8,7 +8,6 @@ package org.whispersystems.textsecuregcm.auth;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -18,16 +17,13 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.textsecuregcm.storage.Account;
|
||||
import org.whispersystems.textsecuregcm.storage.AccountsManager;
|
||||
import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.util.Pair;
|
||||
|
||||
/**
|
||||
* This {@link WebsocketRefreshRequirementProvider} observes intra-request changes in
|
||||
* {@link Device#hasMessageDeliveryChannel()}.
|
||||
* <p>
|
||||
* If a change in any associated {@link Device#hasMessageDeliveryChannel()} is observed, then any active WebSocket
|
||||
* connections for the account must be closed in order for clients to get a refreshed {@link io.dropwizard.auth.Auth}
|
||||
* object with a current device list.
|
||||
* This {@link WebsocketRefreshRequirementProvider} observes intra-request changes in devices linked to an
|
||||
* {@link Account} and triggers a WebSocket refresh if that set changes. If a change in linked devices is observed, then
|
||||
* any active WebSocket connections for the account must be closed in order for clients to get a refreshed
|
||||
* {@link io.dropwizard.auth.Auth} object with a current device list.
|
||||
*
|
||||
* @see AuthenticatedAccount
|
||||
*/
|
||||
@@ -38,55 +34,56 @@ public class AuthEnablementRefreshRequirementProvider implements WebsocketRefres
|
||||
private static final Logger logger = LoggerFactory.getLogger(AuthEnablementRefreshRequirementProvider.class);
|
||||
|
||||
private static final String ACCOUNT_UUID = AuthEnablementRefreshRequirementProvider.class.getName() + ".accountUuid";
|
||||
private static final String DEVICES_ENABLED = AuthEnablementRefreshRequirementProvider.class.getName() + ".devicesEnabled";
|
||||
private static final String LINKED_DEVICE_IDS = AuthEnablementRefreshRequirementProvider.class.getName() + ".deviceIds";
|
||||
|
||||
public AuthEnablementRefreshRequirementProvider(final AccountsManager accountsManager) {
|
||||
this.accountsManager = accountsManager;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void handleRequestFiltered(final RequestEvent requestEvent) {
|
||||
if (requestEvent.getUriInfo().getMatchedResourceMethod().getInvocable().getHandlingMethod().getAnnotation(ChangesDeviceEnabledState.class) != null) {
|
||||
if (requestEvent.getUriInfo().getMatchedResourceMethod().getInvocable().getHandlingMethod().getAnnotation(
|
||||
ChangesLinkedDevices.class) != null) {
|
||||
// The authenticated principal, if any, will be available after filters have run. Now that the account is known,
|
||||
// capture a snapshot of the account's devices before carrying out the request’s business logic.
|
||||
ContainerRequestUtil.getAuthenticatedAccount(requestEvent.getContainerRequest()).ifPresent(account ->
|
||||
setAccount(requestEvent.getContainerRequest(), account));
|
||||
// capture a snapshot of the account's linked devices before carrying out the request’s business logic.
|
||||
ContainerRequestUtil.getAuthenticatedAccount(requestEvent.getContainerRequest())
|
||||
.ifPresent(account -> setAccount(requestEvent.getContainerRequest(), account));
|
||||
}
|
||||
}
|
||||
|
||||
public static void setAccount(final ContainerRequest containerRequest, final Account account) {
|
||||
setAccount(containerRequest, ContainerRequestUtil.AccountInfo.fromAccount(account));
|
||||
}
|
||||
|
||||
private static void setAccount(final ContainerRequest containerRequest, final ContainerRequestUtil.AccountInfo info) {
|
||||
containerRequest.setProperty(ACCOUNT_UUID, info.accountId());
|
||||
containerRequest.setProperty(DEVICES_ENABLED, info.devicesEnabled());
|
||||
containerRequest.setProperty(LINKED_DEVICE_IDS, info.deviceIds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Pair<UUID, Byte>> handleRequestFinished(final RequestEvent requestEvent) {
|
||||
// Now that the request is finished, check whether `hasMessageDeliveryChannel` changed for any of the devices. If
|
||||
// the value did change or if a devices was added or removed, all devices must disconnect and reauthenticate.
|
||||
if (requestEvent.getContainerRequest().getProperty(DEVICES_ENABLED) != null) {
|
||||
// Now that the request is finished, check whether the set of linked devices has changed. If the value did change or
|
||||
// if a devices was added or removed, all devices must disconnect and reauthenticate.
|
||||
if (requestEvent.getContainerRequest().getProperty(LINKED_DEVICE_IDS) != null) {
|
||||
|
||||
@SuppressWarnings("unchecked") final Map<Byte, Boolean> initialDevicesEnabled =
|
||||
(Map<Byte, Boolean>) requestEvent.getContainerRequest().getProperty(DEVICES_ENABLED);
|
||||
@SuppressWarnings("unchecked") final Set<Byte> initialLinkedDeviceIds =
|
||||
(Set<Byte>) requestEvent.getContainerRequest().getProperty(LINKED_DEVICE_IDS);
|
||||
|
||||
return accountsManager.getByAccountIdentifier((UUID) requestEvent.getContainerRequest().getProperty(ACCOUNT_UUID))
|
||||
.map(ContainerRequestUtil.AccountInfo::fromAccount)
|
||||
.map(account -> {
|
||||
.map(accountInfo -> {
|
||||
final Set<Byte> deviceIdsToDisplace;
|
||||
final Map<Byte, Boolean> currentDevicesEnabled = account.devicesEnabled();
|
||||
final Set<Byte> currentLinkedDeviceIds = accountInfo.deviceIds();
|
||||
|
||||
if (!initialDevicesEnabled.equals(currentDevicesEnabled)) {
|
||||
deviceIdsToDisplace = new HashSet<>(initialDevicesEnabled.keySet());
|
||||
deviceIdsToDisplace.addAll(currentDevicesEnabled.keySet());
|
||||
if (!initialLinkedDeviceIds.equals(currentLinkedDeviceIds)) {
|
||||
deviceIdsToDisplace = new HashSet<>(initialLinkedDeviceIds);
|
||||
deviceIdsToDisplace.addAll(currentLinkedDeviceIds);
|
||||
} else {
|
||||
deviceIdsToDisplace = Collections.emptySet();
|
||||
}
|
||||
|
||||
return deviceIdsToDisplace.stream()
|
||||
.map(deviceId -> new Pair<>(account.accountId(), deviceId))
|
||||
.map(deviceId -> new Pair<>(accountInfo.accountId(), deviceId))
|
||||
.collect(Collectors.toList());
|
||||
}).orElseGet(() -> {
|
||||
logger.error("Request had account, but it is no longer present");
|
||||
|
||||
@@ -16,5 +16,5 @@ import java.lang.annotation.Target;
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface ChangesDeviceEnabledState {
|
||||
public @interface ChangesLinkedDevices {
|
||||
}
|
||||
@@ -11,26 +11,23 @@ import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import javax.ws.rs.core.SecurityContext;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class ContainerRequestUtil {
|
||||
|
||||
private static Map<Byte, Boolean> buildDevicesEnabledMap(final Account account) {
|
||||
return account.getDevices().stream().collect(Collectors.toMap(Device::getId, Device::hasMessageDeliveryChannel));
|
||||
}
|
||||
|
||||
/**
|
||||
* A read-only subset of the authenticated Account object, to enforce that filter-based consumers do not perform
|
||||
* account modifying operations.
|
||||
*/
|
||||
record AccountInfo(UUID accountId, String e164, Map<Byte, Boolean> devicesEnabled) {
|
||||
record AccountInfo(UUID accountId, String e164, Set<Byte> deviceIds) {
|
||||
|
||||
static AccountInfo fromAccount(final Account account) {
|
||||
return new AccountInfo(
|
||||
account.getUuid(),
|
||||
account.getNumber(),
|
||||
buildDevicesEnabledMap(account));
|
||||
account.getDevices().stream().map(Device::getId).collect(Collectors.toSet()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user