From cdd03aa64548810591e043fb59a287a1b36c92ba Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Thu, 5 Jan 2023 11:00:50 +0300 Subject: ActivityPub: signed GET requests, fix Signature verification --- .../java/com/juick/server/tests/ServerTests.java | 84 +++++++++++----------- 1 file changed, 44 insertions(+), 40 deletions(-) (limited to 'src/test/java/com/juick/server') diff --git a/src/test/java/com/juick/server/tests/ServerTests.java b/src/test/java/com/juick/server/tests/ServerTests.java index 51ffb876..04a0802a 100644 --- a/src/test/java/com/juick/server/tests/ServerTests.java +++ b/src/test/java/com/juick/server/tests/ServerTests.java @@ -64,6 +64,7 @@ import io.pebbletemplates.pebble.PebbleEngine; import io.pebbletemplates.pebble.error.PebbleException; import io.pebbletemplates.pebble.template.PebbleTemplate; import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; import jakarta.xml.bind.JAXBContext; import jakarta.xml.bind.JAXBException; import jakarta.xml.bind.Marshaller; @@ -85,11 +86,14 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.context.ApplicationEventPublisher; +import org.springframework.core.convert.ConversionService; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.http.*; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.mock.http.client.MockClientHttpRequest; +import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpSession; import org.springframework.mock.web.MockMultipartFile; import org.springframework.test.context.TestPropertySource; @@ -203,7 +207,9 @@ public class ServerTests { @Value("${ios_app_id:}") private String appId; @Inject - private SignatureManager signatureManager; + private ActivityPubService activityPubService; + @Inject + private SignatureService signatureService; @Inject private ActivityPubManager activityPubManager; @Inject @@ -212,8 +218,6 @@ public class ServerTests { private WebApp webApp; @Inject private RestTemplate apClient; - @Inject - private Profile profileController; @Value("classpath:snapshots/activity/testuser.json") private Resource testuserResponse; @@ -274,6 +278,8 @@ public class ServerTests { private User serviceUser; @Inject private User archiveUser; + @Inject + private ConversionService conversionService; private static User ugnich, freefd; static String ugnichName, ugnichPassword, freefdName, freefdPassword; @@ -527,9 +533,6 @@ public class ServerTests { public void testAllUnAuthorized() throws Exception { mockMvc.perform(get("/api/")).andExpect(status().isMovedPermanently()); - mockMvc.perform(get("/api/auth")).andExpect(status().isUnauthorized()) - .andExpect(header().exists("WwW-Authenticate")); - mockMvc.perform(get("/api/home")).andExpect(status().isUnauthorized()); mockMvc.perform(get("/api/messages/recommended")).andExpect(status().isUnauthorized()); @@ -671,20 +674,6 @@ public class ServerTests { .andExpect(status().is4xxClientError()); } - @Test - public void performRequestsWithIssuedToken() throws Exception { - String ugnichHash = userService.getHashByUID(ugnich.getUid()); - mockMvc.perform(get("/api/home")).andExpect(status().isUnauthorized()); - mockMvc.perform(get("/api/auth")).andExpect(status().isUnauthorized()); - mockMvc.perform(get("/api/auth").with(httpBasic(ugnichName, "wrongpassword"))) - .andExpect(status().isUnauthorized()); - MvcResult result = mockMvc.perform(get("/api/auth").with(httpBasic(ugnichName, ugnichPassword))) - .andExpect(status().isOk()).andReturn(); - String authHash = result.getResponse().getContentAsString(); - assertThat(authHash, equalTo(ugnichHash)); - mockMvc.perform(get("/api/home").param("hash", ugnichHash)).andExpect(status().isOk()); - } - @Test public void registerForNotificationsTests() throws Exception { String token = "123456"; @@ -1592,7 +1581,7 @@ public class ServerTests { String userName = "oldschooluser"; String userPassword = ""; userService.createUser(userName, userPassword); - mockMvc.perform(get("/api/auth").with(httpBasic(userName, userPassword))) + mockMvc.perform(get("/api/me").with(httpBasic(userName, userPassword))) .andExpect(status().isUnauthorized()); mockMvc.perform( post("/login") @@ -2060,9 +2049,9 @@ public class ServerTests { create.setId(replyNote.getId()); create.setActor("http://localhost:8080/u/freefd"); create.setObject(replyNote); - signatureManager.post( - (Actor) signatureManager.getContext(URI.create("http://localhost:8080/u/freefd")).get(), - (Actor) signatureManager.getContext(URI.create("http://localhost:8080/u/ugnich")).get(), + activityPubService.post( + (Actor) activityPubService.get(URI.create("http://localhost:8080/u/freefd")).get(), + (Actor) activityPubService.get(URI.create("http://localhost:8080/u/ugnich")).get(), create); Message replyToExt = commandsManager .processCommand(ugnich, String.format("#%d/1 PSSH YOBA ETO TI", msg.getMid()), emptyUri) @@ -2077,13 +2066,20 @@ public class ServerTests { } @Test - public void signingSpec() throws IOException, NoSuchAlgorithmException { - Actor from = (Actor) signatureManager.getContext(URI.create("http://localhost:8080/u/freefd")).get(); - Actor to = (Actor) signatureManager.getContext(URI.create("http://localhost:8080/u/ugnich")).get(); + public void signingSpec() throws Exception { + Actor from = (Actor) activityPubService.get(URI.create("http://localhost:8080/u/freefd")).get(); + Actor to = (Actor) activityPubService.get(URI.create("http://localhost:8080/u/ugnich")).get(); Follow follow = new Follow(); follow.setActor("http://localhost:8080/u/freefd"); follow.setObject(new Context("http://localhost:8080/u/ugnich")); - signatureManager.post(from, to, follow); + var result = activityPubService.post(from, to, follow); + assertThat(result, is(HttpStatusCode.valueOf(202))); + String testuserResponseString = IOUtils.toString(testuserResponse.getInputStream(), + StandardCharsets.UTF_8); + Actor maliciousActor = jsonMapper.readValue(testuserResponseString, Actor.class); + follow.setActor(maliciousActor.getId()); + result = activityPubService.post(maliciousActor, to, follow); + assertThat(result, is(HttpStatusCode.valueOf(401))); } @Test @@ -2093,15 +2089,15 @@ public class ServerTests { Instant now = Instant.now(); String requestDate = DateFormattersHolder.getHttpDateFormatter().format(now); mockMvc.perform(get("/api/me").header("Date", requestDate)).andExpect(status().isUnauthorized()); - String testHost = "localhost"; - Actor ugnichPerson = profileController.getUser("ugnich"); + String testHost = "localhost:8080"; + Actor ugnichPerson = conversionService.convert(ugnich, Actor.class); now = Instant.now(); requestDate = DateFormattersHolder.getHttpDateFormatter().format(now); - String signatureString = signatureManager.addSignature(ugnichPerson, testHost, "GET", meUri, + String signatureString = signatureService.addSignature(ugnichPerson, testHost, "GET", meUri, requestDate, StringUtils.EMPTY); MvcResult me = mockMvc.perform(get("/api/me").header("Host", testHost).header("Date", requestDate) - .header("Signature", signatureString)).andExpect(status().isOk()).andReturn(); + .header( "Signature", signatureString)).andExpect(status().isOk()).andReturn(); User meUser = jsonMapper.readValue(me.getResponse().getContentAsString(), User.class); assertThat(meUser, is(ugnich)); String testuserResponseString = IOUtils.toString(testuserResponse.getInputStream(), @@ -2116,7 +2112,7 @@ public class ServerTests { .andRespond(withSuccess(testuserResponseString, MediaType.APPLICATION_JSON)); restServiceServer.expect(times(4), requestTo(testuserkeyUri)) .andRespond(withSuccess(testuserResponseString, MediaType.APPLICATION_JSON)); - Person testuser = (Person) signatureManager.getContext(testuserUri).get(); + Person testuser = (Person) activityPubService.get(testuserUri).get(); assertThat(testuser.getPublicKey().getPublicKeyPem(), is(testKeystoreManager.getPublicKeyPem())); Instant now2 = Instant.now(); String testRequestDate = DateFormattersHolder.getHttpDateFormatter().format(now2); @@ -2124,7 +2120,7 @@ public class ServerTests { var payload = IOUtils.toByteArray(testfollowRequest.getInputStream()); byte[] digest = MessageDigest.getInstance("SHA-256").digest(payload); // (1) String digestHeader = "SHA-256=" + new String(Base64.encodeBase64(digest)); - String testSignatureString = signatureManager.addSignature(testuser, testHost, "POST", inboxUri, + String testSignatureString = signatureService.addSignature(testuser, testHost, "POST", inboxUri, testRequestDate, digestHeader, testKeystoreManager); mockMvc.perform(post(inboxUri).header("Host", testHost).header("Date", testRequestDate) .header("Digest", digestHeader).header("Signature", testSignatureString) @@ -2149,7 +2145,7 @@ public class ServerTests { var digestHeader = "SHA-256=" + new String(Base64.encodeBase64(digest)); var now2 = Instant.now(); String inboxUri = "/api/inbox"; - String testHost = "localhost"; + String testHost = "localhost:8080"; URI testAppUri = URI.create("https://example.com/actor"); String testappResponseString = IOUtils.toString(testappResponse.getInputStream(), StandardCharsets.UTF_8); @@ -2158,9 +2154,9 @@ public class ServerTests { MockRestServiceServer restServiceServer = MockRestServiceServer.createServer(apClient); restServiceServer.expect(times(2), requestTo(testAppUri)) .andRespond(withSuccess(testappResponseString, MediaType.APPLICATION_JSON)); - Application testapp = (Application) signatureManager.getContext(testAppUri).get(); + Application testapp = (Application) activityPubService.get(testAppUri).get(); assertThat(testapp.getPublicKey().getPublicKeyPem(), is(testKeystoreManager.getPublicKeyPem())); - var testSignatureString = signatureManager.addSignature(testapp, "localhost", "POST", inboxUri, + var testSignatureString = signatureService.addSignature(testapp, testHost, "POST", inboxUri, testRequestDate, digestHeader, testKeystoreManager); mockMvc.perform(post(inboxUri).header("Host", testHost).header("Date", testRequestDate) @@ -2390,18 +2386,18 @@ public class ServerTests { IOUtils.toString(testSuspendedUserResponse.getInputStream(), StandardCharsets.UTF_8), MediaType.APPLICATION_JSON)); - Person testuser = (Person) signatureManager.getContext(URI.create(delete.getObject().getId())).get(); + Person testuser = (Person) activityPubService.get(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, + String testSignatureString = signatureService.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()); + .header("Signature", testSignatureString)).andExpect(status().isAccepted()); apClient.setRequestFactory(originalRequestFactory); Mockito.verify(deleteListener, Mockito.times(1)).onApplicationEvent(deleteEventCaptor.capture()); DeleteUserEvent receivedEvent = deleteEventCaptor.getValue(); @@ -2737,6 +2733,14 @@ public class ServerTests { .andExpect(redirectedUrlPattern("**/settings?continue")).andReturn(); mockMvc.perform(securedResourceAccess.session(session)).andExpect(status().isOk()); } + + @Test + @Transactional + public void signedGetTest() throws Exception { + var protectedAccount = "http://localhost:8080/u/ugnich"; + var context = activityPubService.get(URI.create(protectedAccount)); + assertThat(context.get().getId(), is(protectedAccount)); + } /* @Test public void tokenAuth() throws Exception { -- cgit v1.2.3