mirror of
https://github.com/signalapp/Signal-Server
synced 2026-04-20 13:27:59 +01:00
Allow, but do not require, message delivery to devices without active delivery channels
This commit is contained in:
@@ -22,12 +22,12 @@ import org.whispersystems.textsecuregcm.storage.Device;
|
||||
import org.whispersystems.textsecuregcm.util.Pair;
|
||||
|
||||
/**
|
||||
* This {@link WebsocketRefreshRequirementProvider} observes intra-request changes in {@link Account#isEnabled()} and
|
||||
* This {@link WebsocketRefreshRequirementProvider} observes intra-request changes in
|
||||
* {@link Device#hasMessageDeliveryChannel()}.
|
||||
* <p>
|
||||
* If a change in {@link Account#isEnabled()} or 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.
|
||||
* 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.
|
||||
*
|
||||
* @see AuthenticatedAccount
|
||||
*/
|
||||
@@ -48,9 +48,8 @@ public class AuthEnablementRefreshRequirementProvider implements WebsocketRefres
|
||||
@Override
|
||||
public void handleRequestFiltered(final RequestEvent requestEvent) {
|
||||
if (requestEvent.getUriInfo().getMatchedResourceMethod().getInvocable().getHandlingMethod().getAnnotation(ChangesDeviceEnabledState.class) != null) {
|
||||
// The authenticated principal, if any, will be available after filters have run.
|
||||
// Now that the account is known, capture a snapshot of `isEnabled` for the account's devices before carrying out
|
||||
// the request’s business logic.
|
||||
// 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));
|
||||
}
|
||||
@@ -66,8 +65,8 @@ public class AuthEnablementRefreshRequirementProvider implements WebsocketRefres
|
||||
|
||||
@Override
|
||||
public List<Pair<UUID, Byte>> handleRequestFinished(final RequestEvent requestEvent) {
|
||||
// Now that the request is finished, check whether `isEnabled` 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.
|
||||
// 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) {
|
||||
|
||||
@SuppressWarnings("unchecked") final Map<Byte, Boolean> initialDevicesEnabled =
|
||||
|
||||
@@ -18,6 +18,8 @@ import java.util.Optional;
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
public class OptionalAccess {
|
||||
|
||||
public static String ALL_DEVICES_SELECTOR = "*";
|
||||
|
||||
public static void verify(Optional<Account> requestAccount,
|
||||
Optional<Anonymous> accessKey,
|
||||
Optional<Account> targetAccount,
|
||||
@@ -26,12 +28,12 @@ public class OptionalAccess {
|
||||
try {
|
||||
verify(requestAccount, accessKey, targetAccount);
|
||||
|
||||
if (!deviceSelector.equals("*")) {
|
||||
if (!ALL_DEVICES_SELECTOR.equals(deviceSelector)) {
|
||||
byte deviceId = Byte.parseByte(deviceSelector);
|
||||
|
||||
Optional<Device> targetDevice = targetAccount.get().getDevice(deviceId);
|
||||
|
||||
if (targetDevice.isPresent() && targetDevice.get().hasMessageDeliveryChannel()) {
|
||||
if (targetDevice.isPresent()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -48,11 +50,10 @@ public class OptionalAccess {
|
||||
|
||||
public static void verify(Optional<Account> requestAccount,
|
||||
Optional<Anonymous> accessKey,
|
||||
Optional<Account> targetAccount)
|
||||
{
|
||||
Optional<Account> targetAccount) {
|
||||
if (requestAccount.isPresent()) {
|
||||
// Authenticated requests are never unauthorized; if the target exists and is enabled, return OK, otherwise throw not-found.
|
||||
if (targetAccount.isPresent() && targetAccount.get().isEnabled()) {
|
||||
// Authenticated requests are never unauthorized; if the target exists, return OK, otherwise throw not-found.
|
||||
if (targetAccount.isPresent()) {
|
||||
return;
|
||||
} else {
|
||||
throw new NotFoundException();
|
||||
@@ -63,7 +64,7 @@ public class OptionalAccess {
|
||||
// has unrestricted unidentified access, callers need to supply a fake access key. Likewise, if
|
||||
// the target account does not exist, we *also* report unauthorized here (*not* not-found,
|
||||
// since that would provide a free exists check).
|
||||
if (accessKey.isEmpty() || !targetAccount.map(Account::isEnabled).orElse(false)) {
|
||||
if (accessKey.isEmpty() || targetAccount.isEmpty()) {
|
||||
throw new NotAuthorizedException(Response.Status.UNAUTHORIZED);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user