aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Vitaly Takmazov2021-08-21 03:49:12 +0300
committerGravatar Vitaly Takmazov2021-08-21 03:49:12 +0300
commit389e29881724b90daa466247aef1b8a164511bb9 (patch)
tree66888f12de37dfef24de79bc23c0f4c5fb2939e6
parent880535098d71e80944bbc6496cba692d68c342b6 (diff)
ActivityPub: add Application as an actor type
-rw-r--r--src/main/java/com/juick/KeystoreManager.java4
-rw-r--r--src/main/java/com/juick/SignatureManager.java15
-rw-r--r--src/main/java/com/juick/www/api/activity/model/Context.java3
-rw-r--r--src/main/java/com/juick/www/api/activity/model/objects/Application.java25
-rw-r--r--src/main/java/com/juick/www/api/activity/model/objects/Person.java13
-rw-r--r--src/main/java/com/juick/www/api/activity/model/objects/SecurityObject.java36
-rw-r--r--src/test/java/com/juick/server/tests/ServerTests.java18
-rw-r--r--src/test/resources/flag.json2
-rw-r--r--src/test/resources/snapshots/activity/testapp.json13
9 files changed, 102 insertions, 27 deletions
diff --git a/src/main/java/com/juick/KeystoreManager.java b/src/main/java/com/juick/KeystoreManager.java
index ce7d4a3b..f968f627 100644
--- a/src/main/java/com/juick/KeystoreManager.java
+++ b/src/main/java/com/juick/KeystoreManager.java
@@ -17,7 +17,7 @@
package com.juick;
-import com.juick.www.api.activity.model.objects.Person;
+import com.juick.www.api.activity.model.objects.SecurityObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;
@@ -74,7 +74,7 @@ public class KeystoreManager {
return String.format("-----BEGIN PUBLIC KEY-----\n%s\n-----END PUBLIC KEY-----\n",
String.join("\n", key));
}
- public static PublicKey publicKeyOf(Person person) {
+ public static PublicKey publicKeyOf(SecurityObject person) {
String pubkeyPem = person.getPublicKey().getPublicKeyPem();
String[] rawKey = pubkeyPem.split("\\n");
String pubkeyData = String.join("", Arrays.asList(rawKey).subList(1, rawKey.length - 1));
diff --git a/src/main/java/com/juick/SignatureManager.java b/src/main/java/com/juick/SignatureManager.java
index 35d6215f..d14eedd1 100644
--- a/src/main/java/com/juick/SignatureManager.java
+++ b/src/main/java/com/juick/SignatureManager.java
@@ -24,6 +24,7 @@ import com.juick.service.UserService;
import com.juick.util.DateFormattersHolder;
import com.juick.www.api.activity.model.Context;
import com.juick.www.api.activity.model.objects.Person;
+import com.juick.www.api.activity.model.objects.SecurityObject;
import com.juick.www.api.webfinger.model.Account;
import com.juick.www.api.webfinger.model.Link;
import org.apache.commons.lang3.StringUtils;
@@ -93,14 +94,14 @@ public class SignatureManager {
logger.info("Remote response: {}", response.getStatusCodeValue());
}
- public String addSignature(Person from, String host, String method,
+ public String addSignature(SecurityObject from, String host, String method,
String path, String dateString, String digestHeader)
throws IOException {
return addSignature(from, host, method, path, dateString,
digestHeader, keystoreManager);
}
- public String addSignature(Person from, String host, String method,
+ public String addSignature(SecurityObject from, String host, String method,
String path, String dateString, String digestHeader,
KeystoreManager keystoreManager) throws IOException {
List<String> requiredHeaders = StringUtils.isEmpty(digestHeader) ?
@@ -126,9 +127,9 @@ public class SignatureManager {
Signature signature = Signature.fromString(signatureString);
Optional<Context> context = getContext(UriComponentsBuilder.fromUriString(signature.getKeyId())
.fragment(null).build().toUri());
- if (context.isPresent() && context.get() instanceof Person) {
- Person person = (Person) context.get();
- Key key = KeystoreManager.publicKeyOf(person);
+ if (context.isPresent() && context.get() instanceof SecurityObject) {
+ SecurityObject securityObject = (SecurityObject) context.get();
+ Key key = KeystoreManager.publicKeyOf(securityObject);
Verifier verifier = new Verifier(key, signature);
try {
@@ -136,9 +137,9 @@ public class SignatureManager {
logger.info("signature of {} is valid: {}", signature.getKeyId(), result);
if (result) {
User user = new User();
- user.setUri(URI.create(person.getId()));
+ user.setUri(URI.create(securityObject.getId()));
if (key.equals(keystoreManager.getPublicKey())) {
- return userService.getUserByName(person.getName());
+ return userService.getUserByName(securityObject.getName());
}
return user;
} else {
diff --git a/src/main/java/com/juick/www/api/activity/model/Context.java b/src/main/java/com/juick/www/api/activity/model/Context.java
index a9d61617..fb726565 100644
--- a/src/main/java/com/juick/www/api/activity/model/Context.java
+++ b/src/main/java/com/juick/www/api/activity/model/Context.java
@@ -52,7 +52,8 @@ import java.util.Map;
@JsonSubTypes.Type(value = Note.class, name = "Note"),
@JsonSubTypes.Type(value = OrderedCollection.class, name = "OrderedCollection"),
@JsonSubTypes.Type(value = OrderedCollectionPage.class, name = "OrderedCollectionPage"),
- @JsonSubTypes.Type(value = Person.class, name = "Person")
+ @JsonSubTypes.Type(value = Person.class, name = "Person"),
+ @JsonSubTypes.Type(value = Application.class, name = "Application")
})
public abstract class Context {
diff --git a/src/main/java/com/juick/www/api/activity/model/objects/Application.java b/src/main/java/com/juick/www/api/activity/model/objects/Application.java
new file mode 100644
index 00000000..23a44e49
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/objects/Application.java
@@ -0,0 +1,25 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.objects;
+
+public class Application extends SecurityObject {
+ @Override
+ public String getType() {
+ return "Application";
+ }
+}
diff --git a/src/main/java/com/juick/www/api/activity/model/objects/Person.java b/src/main/java/com/juick/www/api/activity/model/objects/Person.java
index af4ca27d..52626833 100644
--- a/src/main/java/com/juick/www/api/activity/model/objects/Person.java
+++ b/src/main/java/com/juick/www/api/activity/model/objects/Person.java
@@ -18,9 +18,8 @@
package com.juick.www.api.activity.model.objects;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
-import com.juick.www.api.activity.model.Context;
-public class Person extends Context {
+public class Person extends SecurityObject {
private String name;
private String preferredUsername;
@@ -29,7 +28,6 @@ public class Person extends Context {
private String outbox;
private String following;
private String followers;
- private Key publicKey;
@Override
public String getType() {
@@ -85,15 +83,6 @@ public class Person extends Context {
this.followers = followers;
}
- @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
- public Key getPublicKey() {
- return publicKey;
- }
-
- public void setPublicKey(Key publicKey) {
- this.publicKey = publicKey;
- }
-
public String getPreferredUsername() {
return preferredUsername;
}
diff --git a/src/main/java/com/juick/www/api/activity/model/objects/SecurityObject.java b/src/main/java/com/juick/www/api/activity/model/objects/SecurityObject.java
new file mode 100644
index 00000000..8d17aa29
--- /dev/null
+++ b/src/main/java/com/juick/www/api/activity/model/objects/SecurityObject.java
@@ -0,0 +1,36 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+package com.juick.www.api.activity.model.objects;
+
+import com.fasterxml.jackson.annotation.JsonTypeInfo;
+import com.juick.www.api.activity.model.Context;
+
+public class SecurityObject extends Context {
+
+ private Key publicKey;
+
+ @JsonTypeInfo(use = JsonTypeInfo.Id.NONE)
+ public Key getPublicKey() {
+ return publicKey;
+ }
+
+ public void setPublicKey(Key publicKey) {
+ this.publicKey = publicKey;
+ }
+
+}
diff --git a/src/test/java/com/juick/server/tests/ServerTests.java b/src/test/java/com/juick/server/tests/ServerTests.java
index 5b98c41a..e25f0b0d 100644
--- a/src/test/java/com/juick/server/tests/ServerTests.java
+++ b/src/test/java/com/juick/server/tests/ServerTests.java
@@ -39,6 +39,7 @@ import com.juick.www.api.Users;
import com.juick.www.api.activity.Profile;
import com.juick.www.api.activity.model.Context;
import com.juick.www.api.activity.model.activities.*;
+import com.juick.www.api.activity.model.objects.Application;
import com.juick.www.api.activity.model.objects.Note;
import com.juick.www.api.activity.model.objects.Person;
import com.juick.www.api.webfinger.model.Account;
@@ -222,6 +223,8 @@ public class ServerTests {
@Value("classpath:snapshots/activity/testuser.json")
private Resource testuserResponse;
+ @Value("classpath:snapshots/activity/testapp.json")
+ private Resource testappResponse;
@Value("classpath:snapshots/activity/testfollow.json")
private Resource testfollowRequest;
@Value("classpath:snapshots/email/subscription.html")
@@ -1869,14 +1872,19 @@ public class ServerTests {
User meUser = jsonMapper.readValue(me.getResponse().getContentAsString(), User.class);
assertThat(meUser, is(ugnich));
String testuserResponseString = IOUtils.toString(testuserResponse.getInputStream(), StandardCharsets.UTF_8);
+ String testappResponseString = IOUtils.toString(testappResponse.getInputStream(), StandardCharsets.UTF_8);
ClientHttpRequestFactory originalRequestFactory = apClient.getRequestFactory();
URI testuserUri = URI.create("https://example.com/u/testuser");
URI testuserkeyUri = URI.create("https://example.com/u/testuser#main-key");
+ URI testAppUri = URI.create("https://example.com/actor");
+ URI testAppkeyUri = URI.create("https://example.com/actor#main-key");
MockRestServiceServer restServiceServer = MockRestServiceServer.createServer(apClient);
- restServiceServer.expect(times(5), requestTo(testuserUri))
+ restServiceServer.expect(times(4), requestTo(testuserUri))
.andRespond(withSuccess(testuserResponseString, MediaType.APPLICATION_JSON));
- restServiceServer.expect(times(5), requestTo(testuserkeyUri))
+ restServiceServer.expect(times(4), requestTo(testuserkeyUri))
.andRespond(withSuccess(testuserResponseString, MediaType.APPLICATION_JSON));
+ restServiceServer.expect(times(2), requestTo(testAppUri))
+ .andRespond(withSuccess(testappResponseString, MediaType.APPLICATION_JSON));
Person testuser = (Person) signatureManager.getContext(testuserUri).get();
assertThat(testuser.getPublicKey().getPublicKeyPem(), is(testKeystoreManager.getPublicKeyPem()));
Instant now2 = Instant.now();
@@ -1897,13 +1905,15 @@ public class ServerTests {
mockMvc.perform(post(inboxUri).header("Host", testHost).header("Date", testRequestDate)
.header("Signature", testSignatureString).contentType(Context.LD_JSON_MEDIA_TYPE).content(payload))
.andExpect(status().isUnauthorized());
- // test flagging
+ // test flagging as application
payload = IOUtils.toByteArray(flagPayload.getInputStream());
digest = MessageDigest.getInstance("SHA-256").digest(payload); // (1)
digestHeader = "SHA-256=" + new String(Base64.encodeBase64(digest));
now2 = Instant.now();
testRequestDate = DateFormattersHolder.getHttpDateFormatter().format(now2);
- testSignatureString = signatureManager.addSignature(testuser, "localhost", "POST", inboxUri, testRequestDate,
+ Application testapp = (Application) signatureManager.getContext(testAppUri).get();
+ assertThat(testapp.getPublicKey().getPublicKeyPem(), is(testKeystoreManager.getPublicKeyPem()));
+ testSignatureString = signatureManager.addSignature(testapp, "localhost", "POST", inboxUri, testRequestDate,
digestHeader, testKeystoreManager);
mockMvc.perform(post(inboxUri).header("Host", testHost).header("Date", testRequestDate)
.header("Signature", testSignatureString)
diff --git a/src/test/resources/flag.json b/src/test/resources/flag.json
index eae0d15f..c3363f01 100644
--- a/src/test/resources/flag.json
+++ b/src/test/resources/flag.json
@@ -1 +1 @@
-{"@context":"https://www.w3.org/ns/activitystreams","id":"https://localhost/32ac2d3e-c75c-46c1-b0a7-d9fac0986b9a","type":"Flag","actor":"https://example.com/u/testuser","content":"","object":["https://juick.com/u/rtfmpls","https://juick.com/n/2998271-0"]} \ No newline at end of file
+{"@context":"https://www.w3.org/ns/activitystreams","id":"https://localhost/32ac2d3e-c75c-46c1-b0a7-d9fac0986b9a","type":"Flag","actor":"https://example.com/actor","content":"","object":["https://juick.com/u/rtfmpls","https://juick.com/n/2998271-0"]} \ No newline at end of file
diff --git a/src/test/resources/snapshots/activity/testapp.json b/src/test/resources/snapshots/activity/testapp.json
new file mode 100644
index 00000000..d1c2289e
--- /dev/null
+++ b/src/test/resources/snapshots/activity/testapp.json
@@ -0,0 +1,13 @@
+{
+ "@context": [
+ "https://www.w3.org/ns/activitystreams",
+ "https://w3id.org/security/v1"
+ ],
+ "id": "https://example.com/actor",
+ "type": "Application",
+ "publicKey": {
+ "id": "https://example.com/actor#main-key",
+ "owner": "https://example.com/actor",
+ "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"
+ }
+}