diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/auth/OptionalAccess.java b/service/src/main/java/org/whispersystems/textsecuregcm/auth/OptionalAccess.java index c7387bd3a..9a6edb87b 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/auth/OptionalAccess.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/auth/OptionalAccess.java @@ -73,6 +73,11 @@ public class OptionalAccess { return; } + // At this point, any successful authentication requires a real access key on the target account + if (targetAccount.get().getUnidentifiedAccessKey().isEmpty()) { + throw new NotAuthorizedException(Response.Status.UNAUTHORIZED); + } + // Otherwise, access is gated by the caller having the unidentified-access key matching the target account. if (MessageDigest.isEqual(accessKey.get().getAccessKey(), targetAccount.get().getUnidentifiedAccessKey().get())) { return; diff --git a/service/src/test/java/org/whispersystems/textsecuregcm/auth/OptionalAccessTest.java b/service/src/test/java/org/whispersystems/textsecuregcm/auth/OptionalAccessTest.java index f3e2bc81f..06f96671b 100644 --- a/service/src/test/java/org/whispersystems/textsecuregcm/auth/OptionalAccessTest.java +++ b/service/src/test/java/org/whispersystems/textsecuregcm/auth/OptionalAccessTest.java @@ -6,6 +6,7 @@ package org.whispersystems.textsecuregcm.auth; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -115,6 +116,22 @@ class OptionalAccessTest { OptionalAccess.verify(Optional.empty(), Optional.of(new Anonymous(Base64.getEncoder().encodeToString("1234".getBytes()))), Optional.of(account)); } + @Test + void testUnidentifiedTargetMissingAccessKey() { + Account account = mock(Account.class); + when(account.getUnidentifiedAccessKey()).thenReturn(Optional.empty()); + when(account.isEnabled()).thenReturn(true); + try { + OptionalAccess.verify( + Optional.empty(), + Optional.of(new Anonymous(Base64.getEncoder().encodeToString("1234".getBytes()))), + Optional.of(account)); + throw new AssertionError("should fail"); + } catch (WebApplicationException e) { + assertEquals(e.getResponse().getStatus(), 401); + } + } + @Test void testUnidentifiedInactive() { Account account = mock(Account.class);