Clean up concepts of enabled account state

1) Rename "active" methods to be "enabled," since they aren't
   really about "activity."

2) Make authentication fail if a device or account is in dissabled
   state.

3) Let some controllers authenticate accounts that are in a
   disabled state.
This commit is contained in:
Moxie Marlinspike
2019-05-04 12:31:50 -07:00
parent fe66a59618
commit 35116f9229
36 changed files with 570 additions and 231 deletions

View File

@@ -25,6 +25,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.auth.AuthenticationCredentials;
import org.whispersystems.textsecuregcm.auth.AuthorizationHeader;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
import org.whispersystems.textsecuregcm.auth.InvalidAuthorizationHeaderException;
import org.whispersystems.textsecuregcm.auth.StoredVerificationCode;
import org.whispersystems.textsecuregcm.auth.TurnToken;
@@ -63,7 +64,6 @@ import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.util.Arrays;
@@ -260,9 +260,10 @@ public class AccountController {
@PUT
@Path("/gcm/")
@Consumes(MediaType.APPLICATION_JSON)
public void setGcmRegistrationId(@Auth Account account, @Valid GcmRegistrationId registrationId) {
Device device = account.getAuthenticatedDevice().get();
boolean wasAccountActive = account.isActive();
public void setGcmRegistrationId(@Auth DisabledPermittedAccount disabledPermittedAccount, @Valid GcmRegistrationId registrationId) {
Account account = disabledPermittedAccount.getAccount();
Device device = account.getAuthenticatedDevice().get();
boolean wasAccountEnabled = account.isEnabled();
if (device.getGcmId() != null &&
device.getGcmId().equals(registrationId.getGcmRegistrationId()))
@@ -277,7 +278,7 @@ public class AccountController {
accounts.update(account);
if (!wasAccountActive && account.isActive()) {
if (!wasAccountEnabled && account.isEnabled()) {
directoryQueue.addRegisteredUser(account.getNumber());
}
}
@@ -285,14 +286,15 @@ public class AccountController {
@Timed
@DELETE
@Path("/gcm/")
public void deleteGcmRegistrationId(@Auth Account account) {
Device device = account.getAuthenticatedDevice().get();
public void deleteGcmRegistrationId(@Auth DisabledPermittedAccount disabledPermittedAccount) {
Account account = disabledPermittedAccount.getAccount();
Device device = account.getAuthenticatedDevice().get();
device.setGcmId(null);
device.setFetchesMessages(false);
accounts.update(account);
if (!account.isActive()) {
if (!account.isEnabled()) {
directoryQueue.deleteRegisteredUser(account.getNumber());
}
}
@@ -301,9 +303,10 @@ public class AccountController {
@PUT
@Path("/apn/")
@Consumes(MediaType.APPLICATION_JSON)
public void setApnRegistrationId(@Auth Account account, @Valid ApnRegistrationId registrationId) {
Device device = account.getAuthenticatedDevice().get();
boolean wasAccountActive = account.isActive();
public void setApnRegistrationId(@Auth DisabledPermittedAccount disabledPermittedAccount, @Valid ApnRegistrationId registrationId) {
Account account = disabledPermittedAccount.getAccount();
Device device = account.getAuthenticatedDevice().get();
boolean wasAccountEnabled = account.isEnabled();
device.setApnId(registrationId.getApnRegistrationId());
device.setVoipApnId(registrationId.getVoipRegistrationId());
@@ -311,7 +314,7 @@ public class AccountController {
device.setFetchesMessages(false);
accounts.update(account);
if (!wasAccountActive && account.isActive()) {
if (!wasAccountEnabled && account.isEnabled()) {
directoryQueue.addRegisteredUser(account.getNumber());
}
}
@@ -319,14 +322,15 @@ public class AccountController {
@Timed
@DELETE
@Path("/apn/")
public void deleteApnRegistrationId(@Auth Account account) {
Device device = account.getAuthenticatedDevice().get();
public void deleteApnRegistrationId(@Auth DisabledPermittedAccount disabledPermittedAccount) {
Account account = disabledPermittedAccount.getAccount();
Device device = account.getAuthenticatedDevice().get();
device.setApnId(null);
device.setFetchesMessages(false);
accounts.update(account);
if (!account.isActive()) {
if (!account.isEnabled()) {
directoryQueue.deleteRegisteredUser(account.getNumber());
}
}
@@ -351,7 +355,8 @@ public class AccountController {
@Timed
@PUT
@Path("/name/")
public void setName(@Auth Account account, @Valid DeviceName deviceName) {
public void setName(@Auth DisabledPermittedAccount disabledPermittedAccount, @Valid DeviceName deviceName) {
Account account = disabledPermittedAccount.getAccount();
account.getAuthenticatedDevice().get().setName(deviceName.getDeviceName());
accounts.update(account);
}
@@ -359,7 +364,8 @@ public class AccountController {
@Timed
@DELETE
@Path("/signaling_key")
public void removeSignalingKey(@Auth Account account) {
public void removeSignalingKey(@Auth DisabledPermittedAccount disabledPermittedAccount) {
Account account = disabledPermittedAccount.getAccount();
account.getAuthenticatedDevice().get().setSignalingKey(null);
accounts.update(account);
}
@@ -368,11 +374,12 @@ public class AccountController {
@PUT
@Path("/attributes/")
@Consumes(MediaType.APPLICATION_JSON)
public void setAccountAttributes(@Auth Account account,
public void setAccountAttributes(@Auth DisabledPermittedAccount disabledPermittedAccount,
@HeaderParam("X-Signal-Agent") String userAgent,
@Valid AccountAttributes attributes)
{
Device device = account.getAuthenticatedDevice().get();
Account account = disabledPermittedAccount.getAccount();
Device device = account.getAuthenticatedDevice().get();
device.setFetchesMessages(attributes.getFetchesMessages());
device.setName(attributes.getName());
@@ -476,7 +483,7 @@ public class AccountController {
newUserMeter.mark();
}
if (account.isActive()) {
if (account.isEnabled()) {
directoryQueue.addRegisteredUser(number);
} else {
directoryQueue.deleteRegisteredUser(number);

View File

@@ -25,7 +25,6 @@ import org.whispersystems.textsecuregcm.entities.AttachmentUri;
import org.whispersystems.textsecuregcm.limits.RateLimiters;
import org.whispersystems.textsecuregcm.s3.UrlSigner;
import org.whispersystems.textsecuregcm.storage.Account;
import org.whispersystems.textsecuregcm.util.Conversions;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@@ -34,7 +33,6 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.io.IOException;
import java.net.URL;
import java.security.SecureRandom;
import java.util.stream.Stream;
import io.dropwizard.auth.Auth;

View File

@@ -14,7 +14,6 @@ import javax.ws.rs.Produces;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.io.IOException;
import java.security.InvalidKeyException;

View File

@@ -112,7 +112,7 @@ public class DeviceController {
account.removeDevice(deviceId);
accounts.update(account);
if (!account.isActive()) {
if (!account.isEnabled()) {
directoryQueue.deleteRegisteredUser(account.getNumber());
}
@@ -134,7 +134,7 @@ public class DeviceController {
maxDeviceLimit = maxDeviceConfiguration.get(account.getNumber());
}
if (account.getActiveDeviceCount() >= maxDeviceLimit) {
if (account.getEnabledDeviceCount() >= maxDeviceLimit) {
throw new DeviceLimitExceededException(account.getDevices().size(), MAX_DEVICES);
}
@@ -186,7 +186,7 @@ public class DeviceController {
maxDeviceLimit = maxDeviceConfiguration.get(account.get().getNumber());
}
if (account.get().getActiveDeviceCount() >= maxDeviceLimit) {
if (account.get().getEnabledDeviceCount() >= maxDeviceLimit) {
throw new DeviceLimitExceededException(account.get().getDevices().size(), MAX_DEVICES);
}

View File

@@ -20,6 +20,7 @@ import com.codahale.metrics.annotation.Timed;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.textsecuregcm.auth.Anonymous;
import org.whispersystems.textsecuregcm.auth.DisabledPermittedAccount;
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.entities.PreKey;
import org.whispersystems.textsecuregcm.entities.PreKeyCount;
@@ -85,10 +86,11 @@ public class KeysController {
@Timed
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void setKeys(@Auth Account account, @Valid PreKeyState preKeys) {
Device device = account.getAuthenticatedDevice().get();
boolean wasAccountActive = account.isActive();
boolean updateAccount = false;
public void setKeys(@Auth DisabledPermittedAccount disabledPermittedAccount, @Valid PreKeyState preKeys) {
Account account = disabledPermittedAccount.getAccount();
Device device = account.getAuthenticatedDevice().get();
boolean wasAccountEnabled = account.isEnabled();
boolean updateAccount = false;
if (!preKeys.getSignedPreKey().equals(device.getSignedPreKey())) {
device.setSignedPreKey(preKeys.getSignedPreKey());
@@ -103,7 +105,7 @@ public class KeysController {
if (updateAccount) {
accounts.update(account);
if (!wasAccountActive && account.isActive()) {
if (!wasAccountEnabled && account.isEnabled()) {
directoryQueue.addRegisteredUser(account.getNumber());
}
}
@@ -138,7 +140,7 @@ public class KeysController {
List<PreKeyResponseItem> devices = new LinkedList<>();
for (Device device : target.get().getDevices()) {
if (device.isActive() && (deviceId.equals("*") || device.getId() == Long.parseLong(deviceId))) {
if (device.isEnabled() && (deviceId.equals("*") || device.getId() == Long.parseLong(deviceId))) {
SignedPreKey signedPreKey = device.getSignedPreKey();
PreKey preKey = null;
@@ -162,14 +164,15 @@ public class KeysController {
@PUT
@Path("/signed")
@Consumes(MediaType.APPLICATION_JSON)
public void setSignedKey(@Auth Account account, @Valid SignedPreKey signedPreKey) {
Device device = account.getAuthenticatedDevice().get();
boolean wasAccountActive = account.isActive();
public void setSignedKey(@Auth DisabledPermittedAccount disabledPermittedAccount, @Valid SignedPreKey signedPreKey) {
Account account = disabledPermittedAccount.getAccount();
Device device = account.getAuthenticatedDevice().get();
boolean wasAccountEnabled = account.isEnabled();
device.setSignedPreKey(signedPreKey);
accounts.update(account);
if (!wasAccountActive && account.isActive()) {
if (!wasAccountEnabled && account.isEnabled()) {
directoryQueue.addRegisteredUser(account.getNumber());
}
}

View File

@@ -149,7 +149,7 @@ public class MessageController {
}
}
return new SendMessageResponse(!isSyncMessage && source.isPresent() && source.get().getActiveDeviceCount() > 1);
return new SendMessageResponse(!isSyncMessage && source.isPresent() && source.get().getEnabledDeviceCount() > 1);
} catch (NoSuchUserException e) {
throw new WebApplicationException(Response.status(404).build());
} catch (MismatchedDevicesException e) {
@@ -301,7 +301,7 @@ public class MessageController {
}
for (Device device : account.getDevices()) {
if (device.isActive() &&
if (device.isEnabled() &&
!(isSyncMessage && device.getId() == account.getAuthenticatedDevice().get().getId()))
{
accountDeviceIds.add(device.getId());

View File

@@ -10,8 +10,8 @@ import com.codahale.metrics.annotation.Timed;
import org.apache.commons.codec.binary.Base64;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.valuehandling.UnwrapValidatedValue;
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.auth.Anonymous;
import org.whispersystems.textsecuregcm.auth.OptionalAccess;
import org.whispersystems.textsecuregcm.auth.UnidentifiedAccessChecksum;
import org.whispersystems.textsecuregcm.configuration.ProfilesConfiguration;
import org.whispersystems.textsecuregcm.entities.Profile;