From d918967281652ead0130c5dbef663e82003d4393 Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Sun, 22 Aug 2021 10:57:02 +0300 Subject: ActivityPub: handle user deletion for suspended users --- .../java/com/juick/server/MockDeleteListener.java | 30 ++++++++++++++ .../java/com/juick/server/tests/ServerTests.java | 40 +++++++++++++++++- .../snapshots/activity/test_suspended_user.json | 47 ++++++++++++++++++++++ .../resources/snapshots/activity/testdelete.json | 8 ++++ 4 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/juick/server/MockDeleteListener.java create mode 100644 src/test/resources/snapshots/activity/test_suspended_user.json create mode 100644 src/test/resources/snapshots/activity/testdelete.json (limited to 'src/test') diff --git a/src/test/java/com/juick/server/MockDeleteListener.java b/src/test/java/com/juick/server/MockDeleteListener.java new file mode 100644 index 00000000..d060d865 --- /dev/null +++ b/src/test/java/com/juick/server/MockDeleteListener.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2008-2021, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.server; + +import com.juick.service.activities.DeleteUserEvent; +import org.springframework.context.ApplicationListener; + +import javax.annotation.Nonnull; + +public class MockDeleteListener implements ApplicationListener { + @Override + public void onApplicationEvent(@Nonnull DeleteUserEvent event) { + + } +} diff --git a/src/test/java/com/juick/server/tests/ServerTests.java b/src/test/java/com/juick/server/tests/ServerTests.java index 2f60f5be..db1449f2 100644 --- a/src/test/java/com/juick/server/tests/ServerTests.java +++ b/src/test/java/com/juick/server/tests/ServerTests.java @@ -48,6 +48,7 @@ import com.juick.www.api.webfinger.model.Account; import com.juick.www.api.xnodeinfo2.model.NodeInfo; import com.juick.www.WebApp; import com.juick.service.*; +import com.juick.service.activities.DeleteUserEvent; import com.juick.service.activities.UpdateEvent; import com.juick.service.component.SystemEvent; import com.juick.test.util.MockUtils; @@ -229,6 +230,10 @@ public class ServerTests { private Resource testappResponse; @Value("classpath:snapshots/activity/testfollow.json") private Resource testfollowRequest; + @Value("classpath:snapshots/activity/testdelete.json") + private Resource testDeleteRequest; + @Value("classpath:snapshots/activity/test_suspended_user.json") + private Resource testSuspendedUserResponse; @Value("classpath:snapshots/email/subscription.html") private Resource testSubscriptionHtmlEmail; @Value("classpath:snapshots/email/private.html") @@ -2077,7 +2082,7 @@ public class ServerTests { } @Test - public void federatedUserDeletionFlow() throws Exception { + public void federatedUserDeletionFlowWhenItIsGone() throws Exception { String deleteJsonStr = IOUtils.toString(new ClassPathResource("delete_user.json").getURI(), StandardCharsets.UTF_8); Delete delete = jsonMapper.readValue(deleteJsonStr, Delete.class); @@ -2096,6 +2101,39 @@ public class ServerTests { apClient.setRequestFactory(originalRequestFactory); } + @MockBean + private MockDeleteListener deleteListener; + @Captor + protected ArgumentCaptor deleteEventCaptor; + + @Test + public void federatedUserDeletionFlowWhenItIsSuspended() throws Exception { + String deleteJsonStr = IOUtils.toString(testDeleteRequest.getInputStream(), StandardCharsets.UTF_8); + Delete delete = jsonMapper.readValue(deleteJsonStr, Delete.class); + ClientHttpRequestFactory originalRequestFactory = apClient.getRequestFactory(); + MockRestServiceServer restServiceServer = MockRestServiceServer.createServer(apClient); + restServiceServer.expect(times(2), requestTo(delete.getObject().getId())) + .andRespond(withSuccess(IOUtils.toString(testSuspendedUserResponse.getInputStream(), StandardCharsets.UTF_8), MediaType.APPLICATION_JSON)); + Person testuser = (Person) signatureManager.getContext(URI.create(delete.getObject().getId())).get(); + Instant now = Instant.now(); + String testRequestDate = DateFormattersHolder.getHttpDateFormatter().format(now); + String inboxUri = "/api/inbox"; + byte[] digest = MessageDigest.getInstance("SHA-256").digest(deleteJsonStr.getBytes()); + String digestHeader = "SHA-256=" + new String(Base64.encodeBase64(digest)); + String testSignatureString = signatureManager.addSignature(testuser, "localhost", "POST", inboxUri, + testRequestDate, digestHeader, testKeystoreManager); + mockMvc.perform(post(inboxUri).contentType(ACTIVITY_MEDIA_TYPE).content(deleteJsonStr) + .header("Host", "localhost") + .header("Date", testRequestDate) + .header("Digest", digestHeader) + .header("Signature", testSignatureString)) + .andExpect(status().isAccepted()); + apClient.setRequestFactory(originalRequestFactory); + Mockito.verify(deleteListener, Mockito.times(1)).onApplicationEvent(deleteEventCaptor.capture()); + DeleteUserEvent receivedEvent = deleteEventCaptor.getValue(); + assertThat(receivedEvent.getUserUri(), is(testuser.getId())); + } + @Test @Order(2) public void handleIncorrectCertificates() throws Exception { diff --git a/src/test/resources/snapshots/activity/test_suspended_user.json b/src/test/resources/snapshots/activity/test_suspended_user.json new file mode 100644 index 00000000..b0d8e97b --- /dev/null +++ b/src/test/resources/snapshots/activity/test_suspended_user.json @@ -0,0 +1,47 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "toot": "http://joinmastodon.org/ns#", + "featured": { "@id": "toot:featured", "@type": "@id" }, + "featuredTags": { "@id": "toot:featuredTags", "@type": "@id" }, + "alsoKnownAs": { "@id": "as:alsoKnownAs", "@type": "@id" }, + "movedTo": { "@id": "as:movedTo", "@type": "@id" }, + "schema": "http://schema.org#", + "PropertyValue": "schema:PropertyValue", + "value": "schema:value", + "IdentityProof": "toot:IdentityProof", + "discoverable": "toot:discoverable", + "Device": "toot:Device", + "Ed25519Signature": "toot:Ed25519Signature", + "Ed25519Key": "toot:Ed25519Key", + "Curve25519Key": "toot:Curve25519Key", + "EncryptedMessage": "toot:EncryptedMessage", + "publicKeyBase64": "toot:publicKeyBase64", + "deviceId": "toot:deviceId", + "claim": { "@type": "@id", "@id": "toot:claim" }, + "fingerprintKey": { "@type": "@id", "@id": "toot:fingerprintKey" }, + "identityKey": { "@type": "@id", "@id": "toot:identityKey" }, + "devices": { "@type": "@id", "@id": "toot:devices" }, + "messageFranking": "toot:messageFranking", + "messageType": "toot:messageType", + "cipherText": "toot:cipherText", + "suspended": "toot:suspended" + } + ], + "id": "https://example.com/u/testuser", + "type": "Person", + "url": "https://example.com/u/testuser", + "manuallyApprovesFollowers": false, + "discoverable": false, + "suspended": true, + "publicKey": { + "id": "https://example.com/u/testuser#main-key", + "owner": "https://example.com/u/testuser", + "publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAiHKRdKFFeT4P/MVlNbxC\nbbgXOkEdeQzvJB/wAJgSYbUwm9SzNFzttePQXk3/MWoK2awWUInZTduVHsWt8zU7\nO3d9PAW6YH6L1oDkjgMLAb9aUWV2ClQWMwsn88WKK9Rb1WOmd8BrXjPfmeFK2ypQ\n9eg8aKpH36WAXiiaTDfBupBZ0Ki2+E87BrWxpbUeDC1dkV+zbl8BMm7X0rp+reoC\nYUWMcjQMzhMmQOXUd4zwJIDPZDMdF4beq/y6WPSUTVgjs4kPDS1HT60ATnsUqyPE\n6tuGxG4j0msb4TTre87PKxMU5YPOxSiqNL0O/3u9/2shVPpjDa/uy9W+VaeBHbFm\nSQIDAQAB\n-----END PUBLIC KEY-----\n" + }, + "tag": [], + "attachment": [] +} diff --git a/src/test/resources/snapshots/activity/testdelete.json b/src/test/resources/snapshots/activity/testdelete.json new file mode 100644 index 00000000..d7e7af23 --- /dev/null +++ b/src/test/resources/snapshots/activity/testdelete.json @@ -0,0 +1,8 @@ +{ + "@context": "https://www.w3.org/ns/activitystreams", + "id": "https://example.com/u/testuser#delete", + "type": "Delete", + "actor": "https://example.com/u/testuser", + "to": ["https://www.w3.org/ns/activitystreams#Public"], + "object": "https://example.com/u/testuser" +} -- cgit v1.2.3