From 9d32300612adfb33bac19b8a8b8cada32141d53b Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Tue, 9 May 2017 15:43:16 -0700 Subject: [PATCH] The APNs server appears to be lying to us about unregistrations // FREEBIE --- .../textsecuregcm/push/APNSender.java | 16 +- .../tests/push/APNSenderTest.java | 400 +++++++++--------- 2 files changed, 208 insertions(+), 208 deletions(-) diff --git a/src/main/java/org/whispersystems/textsecuregcm/push/APNSender.java b/src/main/java/org/whispersystems/textsecuregcm/push/APNSender.java index 6dce37a9c..b5545343f 100644 --- a/src/main/java/org/whispersystems/textsecuregcm/push/APNSender.java +++ b/src/main/java/org/whispersystems/textsecuregcm/push/APNSender.java @@ -155,14 +155,14 @@ public class APNSender implements Managed { return; } - logger.info("APN Unregister timestamp matches!"); - device.get().setApnId(null); - device.get().setVoipApnId(null); - device.get().setFetchesMessages(false); - accountsManager.update(account.get()); + logger.info("APN Unregister timestamp matches: " + device.get().getApnId() + ", " + device.get().getVoipApnId()); +// device.get().setApnId(null); +// device.get().setVoipApnId(null); +// device.get().setFetchesMessages(false); +// accountsManager.update(account.get()); - if (fallbackManager != null) { - fallbackManager.cancel(new WebsocketAddress(number, deviceId)); - } +// if (fallbackManager != null) { +// fallbackManager.cancel(new WebsocketAddress(number, deviceId)); +// } } } diff --git a/src/test/java/org/whispersystems/textsecuregcm/tests/push/APNSenderTest.java b/src/test/java/org/whispersystems/textsecuregcm/tests/push/APNSenderTest.java index 26248fe77..ed5b9bcab 100644 --- a/src/test/java/org/whispersystems/textsecuregcm/tests/push/APNSenderTest.java +++ b/src/test/java/org/whispersystems/textsecuregcm/tests/push/APNSenderTest.java @@ -124,209 +124,209 @@ public class APNSenderTest { verifyNoMoreInteractions(fallbackManager); } - @Test - public void testUnregisteredUser() throws Exception { - ApnsClient apnsClient = mock(ApnsClient.class); +// @Test +// public void testUnregisteredUser() throws Exception { +// ApnsClient apnsClient = mock(ApnsClient.class); +// +// PushNotificationResponse response = mock(PushNotificationResponse.class); +// when(response.isAccepted()).thenReturn(false); +// when(response.getRejectionReason()).thenReturn("Unregistered"); +// +// DefaultPromise> result = new DefaultPromise<>(executor); +// result.setSuccess(response); +// +// when(apnsClient.sendNotification(any(SimpleApnsPushNotification.class))) +// .thenReturn(result); +// +// RetryingApnsClient retryingApnsClient = new RetryingApnsClient(apnsClient, 10); +// ApnMessage message = new ApnMessage(DESTINATION_APN_ID, DESTINATION_NUMBER, 1, "message", true, 30); +// APNSender apnSender = new APNSender(new SynchronousExecutorService(), accountsManager, retryingApnsClient, "foo", false); +// apnSender.setApnFallbackManager(fallbackManager); +// +// when(destinationDevice.getApnId()).thenReturn(DESTINATION_APN_ID); +// when(destinationDevice.getPushTimestamp()).thenReturn(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(11)); +// +// ListenableFuture sendFuture = apnSender.sendMessage(message); +// ApnResult apnResult = sendFuture.get(); +// +// Thread.sleep(1000); // =( +// +// ArgumentCaptor notification = ArgumentCaptor.forClass(SimpleApnsPushNotification.class); +// verify(apnsClient, times(1)).sendNotification(notification.capture()); +// +// assertThat(notification.getValue().getToken()).isEqualTo(DESTINATION_APN_ID); +// assertThat(notification.getValue().getExpiration()).isEqualTo(new Date(30)); +// assertThat(notification.getValue().getPayload()).isEqualTo("message"); +// assertThat(notification.getValue().getPriority()).isEqualTo(DeliveryPriority.IMMEDIATE); +// +// assertThat(apnResult.getStatus()).isEqualTo(ApnResult.Status.NO_SUCH_USER); +// +// verifyNoMoreInteractions(apnsClient); +// verify(accountsManager, times(1)).get(eq(DESTINATION_NUMBER)); +// verify(destinationAccount, times(1)).getDevice(1); +// verify(destinationDevice, times(1)).getApnId(); +// verify(destinationDevice, times(1)).getPushTimestamp(); +// verify(destinationDevice, times(1)).setApnId(eq((String)null)); +// verify(destinationDevice, times(1)).setVoipApnId(eq((String)null)); +// verify(destinationDevice, times(1)).setFetchesMessages(eq(false)); +// verify(accountsManager, times(1)).update(eq(destinationAccount)); +// verify(fallbackManager, times(1)).cancel(eq(new WebsocketAddress(DESTINATION_NUMBER, 1))); +// +// verifyNoMoreInteractions(accountsManager); +// verifyNoMoreInteractions(fallbackManager); +// } - PushNotificationResponse response = mock(PushNotificationResponse.class); - when(response.isAccepted()).thenReturn(false); - when(response.getRejectionReason()).thenReturn("Unregistered"); +// @Test +// public void testVoipUnregisteredUser() throws Exception { +// ApnsClient apnsClient = mock(ApnsClient.class); +// +// PushNotificationResponse response = mock(PushNotificationResponse.class); +// when(response.isAccepted()).thenReturn(false); +// when(response.getRejectionReason()).thenReturn("Unregistered"); +// +// DefaultPromise> result = new DefaultPromise<>(executor); +// result.setSuccess(response); +// +// when(apnsClient.sendNotification(any(SimpleApnsPushNotification.class))) +// .thenReturn(result); +// +// RetryingApnsClient retryingApnsClient = new RetryingApnsClient(apnsClient, 10); +// ApnMessage message = new ApnMessage(DESTINATION_APN_ID, DESTINATION_NUMBER, 1, "message", true, 30); +// APNSender apnSender = new APNSender(new SynchronousExecutorService(), accountsManager, retryingApnsClient, "foo", false); +// apnSender.setApnFallbackManager(fallbackManager); +// +// when(destinationDevice.getApnId()).thenReturn("baz"); +// when(destinationDevice.getVoipApnId()).thenReturn(DESTINATION_APN_ID); +// when(destinationDevice.getPushTimestamp()).thenReturn(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(11)); +// +// ListenableFuture sendFuture = apnSender.sendMessage(message); +// ApnResult apnResult = sendFuture.get(); +// +// Thread.sleep(1000); // =( +// +// ArgumentCaptor notification = ArgumentCaptor.forClass(SimpleApnsPushNotification.class); +// verify(apnsClient, times(1)).sendNotification(notification.capture()); +// +// assertThat(notification.getValue().getToken()).isEqualTo(DESTINATION_APN_ID); +// assertThat(notification.getValue().getExpiration()).isEqualTo(new Date(30)); +// assertThat(notification.getValue().getPayload()).isEqualTo("message"); +// assertThat(notification.getValue().getPriority()).isEqualTo(DeliveryPriority.IMMEDIATE); +// +// assertThat(apnResult.getStatus()).isEqualTo(ApnResult.Status.NO_SUCH_USER); +// +// verifyNoMoreInteractions(apnsClient); +// verify(accountsManager, times(1)).get(eq(DESTINATION_NUMBER)); +// verify(destinationAccount, times(1)).getDevice(1); +// verify(destinationDevice, times(1)).getApnId(); +// verify(destinationDevice, times(1)).getVoipApnId(); +// verify(destinationDevice, times(1)).getPushTimestamp(); +// verify(destinationDevice, times(1)).setApnId(eq((String)null)); +// verify(destinationDevice, times(1)).setVoipApnId(eq((String)null)); +// verify(destinationDevice, times(1)).setFetchesMessages(eq(false)); +// verify(accountsManager, times(1)).update(eq(destinationAccount)); +// verify(fallbackManager, times(1)).cancel(eq(new WebsocketAddress(DESTINATION_NUMBER, 1))); +// +// verifyNoMoreInteractions(accountsManager); +// verifyNoMoreInteractions(fallbackManager); +// } - DefaultPromise> result = new DefaultPromise<>(executor); - result.setSuccess(response); +// @Test +// public void testRecentUnregisteredUser() throws Exception { +// ApnsClient apnsClient = mock(ApnsClient.class); +// +// PushNotificationResponse response = mock(PushNotificationResponse.class); +// when(response.isAccepted()).thenReturn(false); +// when(response.getRejectionReason()).thenReturn("Unregistered"); +// +// DefaultPromise> result = new DefaultPromise<>(executor); +// result.setSuccess(response); +// +// when(apnsClient.sendNotification(any(SimpleApnsPushNotification.class))) +// .thenReturn(result); +// +// RetryingApnsClient retryingApnsClient = new RetryingApnsClient(apnsClient, 10); +// ApnMessage message = new ApnMessage(DESTINATION_APN_ID, DESTINATION_NUMBER, 1, "message", true, 30); +// APNSender apnSender = new APNSender(new SynchronousExecutorService(), accountsManager, retryingApnsClient, "foo", false); +// apnSender.setApnFallbackManager(fallbackManager); +// +// when(destinationDevice.getApnId()).thenReturn(DESTINATION_APN_ID); +// when(destinationDevice.getPushTimestamp()).thenReturn(System.currentTimeMillis()); +// +// ListenableFuture sendFuture = apnSender.sendMessage(message); +// ApnResult apnResult = sendFuture.get(); +// +// Thread.sleep(1000); // =( +// +// ArgumentCaptor notification = ArgumentCaptor.forClass(SimpleApnsPushNotification.class); +// verify(apnsClient, times(1)).sendNotification(notification.capture()); +// +// assertThat(notification.getValue().getToken()).isEqualTo(DESTINATION_APN_ID); +// assertThat(notification.getValue().getExpiration()).isEqualTo(new Date(30)); +// assertThat(notification.getValue().getPayload()).isEqualTo("message"); +// assertThat(notification.getValue().getPriority()).isEqualTo(DeliveryPriority.IMMEDIATE); +// +// assertThat(apnResult.getStatus()).isEqualTo(ApnResult.Status.NO_SUCH_USER); +// +// verifyNoMoreInteractions(apnsClient); +// verify(accountsManager, times(1)).get(eq(DESTINATION_NUMBER)); +// verify(destinationAccount, times(1)).getDevice(1); +// verify(destinationDevice, times(1)).getApnId(); +// verify(destinationDevice, times(1)).getPushTimestamp(); +// +// verifyNoMoreInteractions(destinationDevice); +// verifyNoMoreInteractions(destinationAccount); +// verifyNoMoreInteractions(accountsManager); +// verifyNoMoreInteractions(fallbackManager); +// } - when(apnsClient.sendNotification(any(SimpleApnsPushNotification.class))) - .thenReturn(result); - - RetryingApnsClient retryingApnsClient = new RetryingApnsClient(apnsClient, 10); - ApnMessage message = new ApnMessage(DESTINATION_APN_ID, DESTINATION_NUMBER, 1, "message", true, 30); - APNSender apnSender = new APNSender(new SynchronousExecutorService(), accountsManager, retryingApnsClient, "foo", false); - apnSender.setApnFallbackManager(fallbackManager); - - when(destinationDevice.getApnId()).thenReturn(DESTINATION_APN_ID); - when(destinationDevice.getPushTimestamp()).thenReturn(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(11)); - - ListenableFuture sendFuture = apnSender.sendMessage(message); - ApnResult apnResult = sendFuture.get(); - - Thread.sleep(1000); // =( - - ArgumentCaptor notification = ArgumentCaptor.forClass(SimpleApnsPushNotification.class); - verify(apnsClient, times(1)).sendNotification(notification.capture()); - - assertThat(notification.getValue().getToken()).isEqualTo(DESTINATION_APN_ID); - assertThat(notification.getValue().getExpiration()).isEqualTo(new Date(30)); - assertThat(notification.getValue().getPayload()).isEqualTo("message"); - assertThat(notification.getValue().getPriority()).isEqualTo(DeliveryPriority.IMMEDIATE); - - assertThat(apnResult.getStatus()).isEqualTo(ApnResult.Status.NO_SUCH_USER); - - verifyNoMoreInteractions(apnsClient); - verify(accountsManager, times(1)).get(eq(DESTINATION_NUMBER)); - verify(destinationAccount, times(1)).getDevice(1); - verify(destinationDevice, times(1)).getApnId(); - verify(destinationDevice, times(1)).getPushTimestamp(); - verify(destinationDevice, times(1)).setApnId(eq((String)null)); - verify(destinationDevice, times(1)).setVoipApnId(eq((String)null)); - verify(destinationDevice, times(1)).setFetchesMessages(eq(false)); - verify(accountsManager, times(1)).update(eq(destinationAccount)); - verify(fallbackManager, times(1)).cancel(eq(new WebsocketAddress(DESTINATION_NUMBER, 1))); - - verifyNoMoreInteractions(accountsManager); - verifyNoMoreInteractions(fallbackManager); - } - - @Test - public void testVoipUnregisteredUser() throws Exception { - ApnsClient apnsClient = mock(ApnsClient.class); - - PushNotificationResponse response = mock(PushNotificationResponse.class); - when(response.isAccepted()).thenReturn(false); - when(response.getRejectionReason()).thenReturn("Unregistered"); - - DefaultPromise> result = new DefaultPromise<>(executor); - result.setSuccess(response); - - when(apnsClient.sendNotification(any(SimpleApnsPushNotification.class))) - .thenReturn(result); - - RetryingApnsClient retryingApnsClient = new RetryingApnsClient(apnsClient, 10); - ApnMessage message = new ApnMessage(DESTINATION_APN_ID, DESTINATION_NUMBER, 1, "message", true, 30); - APNSender apnSender = new APNSender(new SynchronousExecutorService(), accountsManager, retryingApnsClient, "foo", false); - apnSender.setApnFallbackManager(fallbackManager); - - when(destinationDevice.getApnId()).thenReturn("baz"); - when(destinationDevice.getVoipApnId()).thenReturn(DESTINATION_APN_ID); - when(destinationDevice.getPushTimestamp()).thenReturn(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(11)); - - ListenableFuture sendFuture = apnSender.sendMessage(message); - ApnResult apnResult = sendFuture.get(); - - Thread.sleep(1000); // =( - - ArgumentCaptor notification = ArgumentCaptor.forClass(SimpleApnsPushNotification.class); - verify(apnsClient, times(1)).sendNotification(notification.capture()); - - assertThat(notification.getValue().getToken()).isEqualTo(DESTINATION_APN_ID); - assertThat(notification.getValue().getExpiration()).isEqualTo(new Date(30)); - assertThat(notification.getValue().getPayload()).isEqualTo("message"); - assertThat(notification.getValue().getPriority()).isEqualTo(DeliveryPriority.IMMEDIATE); - - assertThat(apnResult.getStatus()).isEqualTo(ApnResult.Status.NO_SUCH_USER); - - verifyNoMoreInteractions(apnsClient); - verify(accountsManager, times(1)).get(eq(DESTINATION_NUMBER)); - verify(destinationAccount, times(1)).getDevice(1); - verify(destinationDevice, times(1)).getApnId(); - verify(destinationDevice, times(1)).getVoipApnId(); - verify(destinationDevice, times(1)).getPushTimestamp(); - verify(destinationDevice, times(1)).setApnId(eq((String)null)); - verify(destinationDevice, times(1)).setVoipApnId(eq((String)null)); - verify(destinationDevice, times(1)).setFetchesMessages(eq(false)); - verify(accountsManager, times(1)).update(eq(destinationAccount)); - verify(fallbackManager, times(1)).cancel(eq(new WebsocketAddress(DESTINATION_NUMBER, 1))); - - verifyNoMoreInteractions(accountsManager); - verifyNoMoreInteractions(fallbackManager); - } - - @Test - public void testRecentUnregisteredUser() throws Exception { - ApnsClient apnsClient = mock(ApnsClient.class); - - PushNotificationResponse response = mock(PushNotificationResponse.class); - when(response.isAccepted()).thenReturn(false); - when(response.getRejectionReason()).thenReturn("Unregistered"); - - DefaultPromise> result = new DefaultPromise<>(executor); - result.setSuccess(response); - - when(apnsClient.sendNotification(any(SimpleApnsPushNotification.class))) - .thenReturn(result); - - RetryingApnsClient retryingApnsClient = new RetryingApnsClient(apnsClient, 10); - ApnMessage message = new ApnMessage(DESTINATION_APN_ID, DESTINATION_NUMBER, 1, "message", true, 30); - APNSender apnSender = new APNSender(new SynchronousExecutorService(), accountsManager, retryingApnsClient, "foo", false); - apnSender.setApnFallbackManager(fallbackManager); - - when(destinationDevice.getApnId()).thenReturn(DESTINATION_APN_ID); - when(destinationDevice.getPushTimestamp()).thenReturn(System.currentTimeMillis()); - - ListenableFuture sendFuture = apnSender.sendMessage(message); - ApnResult apnResult = sendFuture.get(); - - Thread.sleep(1000); // =( - - ArgumentCaptor notification = ArgumentCaptor.forClass(SimpleApnsPushNotification.class); - verify(apnsClient, times(1)).sendNotification(notification.capture()); - - assertThat(notification.getValue().getToken()).isEqualTo(DESTINATION_APN_ID); - assertThat(notification.getValue().getExpiration()).isEqualTo(new Date(30)); - assertThat(notification.getValue().getPayload()).isEqualTo("message"); - assertThat(notification.getValue().getPriority()).isEqualTo(DeliveryPriority.IMMEDIATE); - - assertThat(apnResult.getStatus()).isEqualTo(ApnResult.Status.NO_SUCH_USER); - - verifyNoMoreInteractions(apnsClient); - verify(accountsManager, times(1)).get(eq(DESTINATION_NUMBER)); - verify(destinationAccount, times(1)).getDevice(1); - verify(destinationDevice, times(1)).getApnId(); - verify(destinationDevice, times(1)).getPushTimestamp(); - - verifyNoMoreInteractions(destinationDevice); - verifyNoMoreInteractions(destinationAccount); - verifyNoMoreInteractions(accountsManager); - verifyNoMoreInteractions(fallbackManager); - } - - @Test - public void testUnregisteredUserOldApnId() throws Exception { - ApnsClient apnsClient = mock(ApnsClient.class); - - PushNotificationResponse response = mock(PushNotificationResponse.class); - when(response.isAccepted()).thenReturn(false); - when(response.getRejectionReason()).thenReturn("Unregistered"); - - DefaultPromise> result = new DefaultPromise<>(executor); - result.setSuccess(response); - - when(apnsClient.sendNotification(any(SimpleApnsPushNotification.class))) - .thenReturn(result); - - RetryingApnsClient retryingApnsClient = new RetryingApnsClient(apnsClient, 10); - ApnMessage message = new ApnMessage(DESTINATION_APN_ID, DESTINATION_NUMBER, 1, "message", true, 30); - APNSender apnSender = new APNSender(new SynchronousExecutorService(), accountsManager, retryingApnsClient, "foo", false); - apnSender.setApnFallbackManager(fallbackManager); - - when(destinationDevice.getApnId()).thenReturn("baz"); - when(destinationDevice.getPushTimestamp()).thenReturn(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(12)); - - ListenableFuture sendFuture = apnSender.sendMessage(message); - ApnResult apnResult = sendFuture.get(); - - Thread.sleep(1000); // =( - - ArgumentCaptor notification = ArgumentCaptor.forClass(SimpleApnsPushNotification.class); - verify(apnsClient, times(1)).sendNotification(notification.capture()); - - assertThat(notification.getValue().getToken()).isEqualTo(DESTINATION_APN_ID); - assertThat(notification.getValue().getExpiration()).isEqualTo(new Date(30)); - assertThat(notification.getValue().getPayload()).isEqualTo("message"); - assertThat(notification.getValue().getPriority()).isEqualTo(DeliveryPriority.IMMEDIATE); - - assertThat(apnResult.getStatus()).isEqualTo(ApnResult.Status.NO_SUCH_USER); - - verifyNoMoreInteractions(apnsClient); - verify(accountsManager, times(1)).get(eq(DESTINATION_NUMBER)); - verify(destinationAccount, times(1)).getDevice(1); - verify(destinationDevice, times(2)).getApnId(); - verify(destinationDevice, times(2)).getVoipApnId(); - - verifyNoMoreInteractions(destinationDevice); - verifyNoMoreInteractions(destinationAccount); - verifyNoMoreInteractions(accountsManager); - verifyNoMoreInteractions(fallbackManager); - } +// @Test +// public void testUnregisteredUserOldApnId() throws Exception { +// ApnsClient apnsClient = mock(ApnsClient.class); +// +// PushNotificationResponse response = mock(PushNotificationResponse.class); +// when(response.isAccepted()).thenReturn(false); +// when(response.getRejectionReason()).thenReturn("Unregistered"); +// +// DefaultPromise> result = new DefaultPromise<>(executor); +// result.setSuccess(response); +// +// when(apnsClient.sendNotification(any(SimpleApnsPushNotification.class))) +// .thenReturn(result); +// +// RetryingApnsClient retryingApnsClient = new RetryingApnsClient(apnsClient, 10); +// ApnMessage message = new ApnMessage(DESTINATION_APN_ID, DESTINATION_NUMBER, 1, "message", true, 30); +// APNSender apnSender = new APNSender(new SynchronousExecutorService(), accountsManager, retryingApnsClient, "foo", false); +// apnSender.setApnFallbackManager(fallbackManager); +// +// when(destinationDevice.getApnId()).thenReturn("baz"); +// when(destinationDevice.getPushTimestamp()).thenReturn(System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(12)); +// +// ListenableFuture sendFuture = apnSender.sendMessage(message); +// ApnResult apnResult = sendFuture.get(); +// +// Thread.sleep(1000); // =( +// +// ArgumentCaptor notification = ArgumentCaptor.forClass(SimpleApnsPushNotification.class); +// verify(apnsClient, times(1)).sendNotification(notification.capture()); +// +// assertThat(notification.getValue().getToken()).isEqualTo(DESTINATION_APN_ID); +// assertThat(notification.getValue().getExpiration()).isEqualTo(new Date(30)); +// assertThat(notification.getValue().getPayload()).isEqualTo("message"); +// assertThat(notification.getValue().getPriority()).isEqualTo(DeliveryPriority.IMMEDIATE); +// +// assertThat(apnResult.getStatus()).isEqualTo(ApnResult.Status.NO_SUCH_USER); +// +// verifyNoMoreInteractions(apnsClient); +// verify(accountsManager, times(1)).get(eq(DESTINATION_NUMBER)); +// verify(destinationAccount, times(1)).getDevice(1); +// verify(destinationDevice, times(2)).getApnId(); +// verify(destinationDevice, times(2)).getVoipApnId(); +// +// verifyNoMoreInteractions(destinationDevice); +// verifyNoMoreInteractions(destinationAccount); +// verifyNoMoreInteractions(accountsManager); +// verifyNoMoreInteractions(fallbackManager); +// } @Test public void testGenericFailure() throws Exception {