diff options
Diffstat (limited to 'juick-server/src/main/java/com/juick/server/api/activity')
17 files changed, 211 insertions, 116 deletions
diff --git a/juick-server/src/main/java/com/juick/server/api/activity/Profile.java b/juick-server/src/main/java/com/juick/server/api/activity/Profile.java index 9f98b4ea..656d85dd 100644 --- a/juick-server/src/main/java/com/juick/server/api/activity/Profile.java +++ b/juick-server/src/main/java/com/juick/server/api/activity/Profile.java @@ -2,24 +2,33 @@ package com.juick.server.api.activity; import com.juick.User; import com.juick.server.KeystoreManager; +import com.juick.server.SignatureManager; import com.juick.server.api.activity.model.*; +import com.juick.server.api.activity.model.activities.Create; +import com.juick.server.api.activity.model.activities.Follow; +import com.juick.server.api.activity.model.activities.Undo; import com.juick.server.util.HttpNotFoundException; import com.juick.server.util.UserUtils; import com.juick.service.MessagesService; import com.juick.service.UserService; +import com.juick.service.activities.FollowEvent; import com.juick.util.MessageUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; import javax.inject.Inject; +import java.net.URI; import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -31,6 +40,10 @@ public class Profile { private MessagesService messagesService; @Inject private KeystoreManager keystoreManager; + @Inject + private SignatureManager signatureManager; + @Inject + private ApplicationEventPublisher applicationEventPublisher; @Value("${web_domain:localhost}") private String domain; @Value("${ap_base_uri:http://localhost:8080/}") @@ -38,7 +51,7 @@ public class Profile { @Value("${img_url:http://localhost:8080/i/}") private String baseImagesUri; - @GetMapping(value = "/u/{userName}", produces = { ActivityObject.LD_JSON_MEDIA_TYPE, ActivityObject.ACTIVITY_JSON_MEDIA_TYPE }) + @GetMapping(value = "/u/{userName}", produces = { Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITY_JSON_MEDIA_TYPE }) public Person getUser(@PathVariable String userName) { User user = userService.getUserByName(userName); if (!user.isAnonymous()) { @@ -52,9 +65,9 @@ public class Profile { Key publicKey = new Key(); publicKey.setId(person.getId() + "#main-key"); publicKey.setOwner(person.getId()); - publicKey.setPublicKeyPem(keystoreManager.getPublicKey()); + publicKey.setPublicKeyPem(keystoreManager.getPublicKeyPem()); person.setPublicKey(publicKey); - uri.replacePath("/post"); + uri.replacePath("/api/inbox"); person.setInbox(uri.toUriString()); person.setOutbox(uri.replacePath(String.format("/u/%s/blog/toc", userName)).toUriString()); person.setFollowers(uri.replacePath(String.format("/u/%s/followers/toc", userName)).toUriString()); @@ -65,11 +78,11 @@ public class Profile { avatar.setUrl(image.toUriString()); avatar.setMediaType("image/png"); person.setIcon(avatar); - return (Person) ActivityObject.build(person); + return (Person) Context.build(person); } throw new HttpNotFoundException(); } - @GetMapping(value = "/u/{userName}/blog/toc", produces = { ActivityObject.LD_JSON_MEDIA_TYPE, ActivityObject.ACTIVITY_JSON_MEDIA_TYPE }) + @GetMapping(value = "/u/{userName}/blog/toc", produces = { Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITY_JSON_MEDIA_TYPE }) public OrderedCollection getOutbox(@PathVariable String userName) { User user = userService.getUserByName(userName); if (!user.isAnonymous()) { @@ -78,11 +91,11 @@ public class Profile { blog.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString()); blog.setTotalItems(userService.getStatsMessages(user.getUid())); blog.setFirst(uriComponentsBuilder.path(String.format("/u/%s/blog", userName)).toUriString()); - return (OrderedCollection) ActivityObject.build(blog); + return (OrderedCollection) Context.build(blog); } throw new HttpNotFoundException(); } - @GetMapping(value = "/u/{userName}/blog", produces = { ActivityObject.LD_JSON_MEDIA_TYPE, ActivityObject.ACTIVITY_JSON_MEDIA_TYPE }) + @GetMapping(value = "/u/{userName}/blog", produces = { Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITY_JSON_MEDIA_TYPE }) public OrderedCollectionPage getOutboxPage(@PathVariable String userName, @RequestParam(required = false, defaultValue = "0") int before) { User visitor = UserUtils.getCurrentUser(); @@ -127,11 +140,11 @@ public class Profile { page.setNext(uri.queryParam("before", beforeNext).toUriString()); } page.setLast(uri.replaceQueryParam("before", "1").toUriString()); - return (OrderedCollectionPage) ActivityObject.build(page); + return (OrderedCollectionPage) Context.build(page); } throw new HttpNotFoundException(); } - @GetMapping(value = "/u/{userName}/followers/toc", produces = { ActivityObject.LD_JSON_MEDIA_TYPE, ActivityObject.ACTIVITY_JSON_MEDIA_TYPE }) + @GetMapping(value = "/u/{userName}/followers/toc", produces = { Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITY_JSON_MEDIA_TYPE }) public OrderedCollection getFollowers(@PathVariable String userName) { User user = userService.getUserByName(userName); if (!user.isAnonymous()) { @@ -140,11 +153,11 @@ public class Profile { followers.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString()); followers.setTotalItems(userService.getStatsMyReaders(user.getUid())); followers.setFirst(uriComponentsBuilder.path(String.format("/u/%s/followers", userName)).toUriString()); - return (OrderedCollection) ActivityObject.build(followers); + return (OrderedCollection) Context.build(followers); } throw new HttpNotFoundException(); } - @GetMapping(value = "/u/{userName}/followers", produces = { ActivityObject.LD_JSON_MEDIA_TYPE, ActivityObject.ACTIVITY_JSON_MEDIA_TYPE }) + @GetMapping(value = "/u/{userName}/followers", produces = { Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITY_JSON_MEDIA_TYPE }) public OrderedCollectionPage getFollowersPage(@PathVariable String userName, @RequestParam(required = false, defaultValue = "0") int page) { User user = userService.getUserByName(userName); @@ -169,11 +182,11 @@ public class Profile { if (hasNext) { result.setNext(uriComponentsBuilder.queryParam("page", page + 1).toUriString()); } - return (OrderedCollectionPage) ActivityObject.build(result); + return (OrderedCollectionPage) Context.build(result); } throw new HttpNotFoundException(); } - @GetMapping(value = "/u/{userName}/following/toc", produces = { ActivityObject.LD_JSON_MEDIA_TYPE, ActivityObject.ACTIVITY_JSON_MEDIA_TYPE }) + @GetMapping(value = "/u/{userName}/following/toc", produces = { Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITY_JSON_MEDIA_TYPE }) public OrderedCollection getFollowing(@PathVariable String userName) { User user = userService.getUserByName(userName); if (!user.isAnonymous()) { @@ -182,11 +195,11 @@ public class Profile { following.setId(ServletUriComponentsBuilder.fromCurrentRequestUri().toUriString()); following.setTotalItems(userService.getUserFriends(user.getUid()).size()); following.setFirst(uriComponentsBuilder.path(String.format("/u/%s/followers", userName)).toUriString()); - return (OrderedCollection) ActivityObject.build(following); + return (OrderedCollection) Context.build(following); } throw new HttpNotFoundException(); } - @GetMapping(value = "/u/{userName}/following", produces = { ActivityObject.LD_JSON_MEDIA_TYPE, ActivityObject.ACTIVITY_JSON_MEDIA_TYPE }) + @GetMapping(value = "/u/{userName}/following", produces = { Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITY_JSON_MEDIA_TYPE }) public OrderedCollectionPage getFollowingPage(@PathVariable String userName, @RequestParam(required = false, defaultValue = "0") int page) { User user = userService.getUserByName(userName); @@ -211,8 +224,37 @@ public class Profile { if (hasNext) { result.setNext(uriComponentsBuilder.queryParam("page", page + 1).toUriString()); } - return (OrderedCollectionPage) ActivityObject.build(result); + return (OrderedCollectionPage) Context.build(result); } throw new HttpNotFoundException(); } + @PostMapping(value = "/api/inbox", consumes = { Context.LD_JSON_MEDIA_TYPE, Context.ACTIVITY_JSON_MEDIA_TYPE }) + public ResponseEntity<Void> processInbox(@RequestBody Context activity, + @RequestHeader(name = "Host") String host, + @RequestHeader(name = "Date") String date, + @RequestHeader(name = "Digest") String digest, + @RequestHeader(name = "Content-Type") String contentType, + @RequestHeader(name = "Signature") String signature) { + if (activity instanceof Follow) { + Follow followRequest = (Follow) activity; + UriComponents componentsBuilder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); + Map<String, String> headers = new HashMap<>(); + headers.put("host", host); + headers.put("date", date); + headers.put("digest", digest); + headers.put("content-type", contentType); + boolean valid = signatureManager.verifySignature(signature, URI.create(followRequest.getActor()), "POST", + componentsBuilder.getPath(), headers); + if (valid) { + applicationEventPublisher.publishEvent( + new FollowEvent(this, followRequest)); + return new ResponseEntity<>(HttpStatus.ACCEPTED); + } + return new ResponseEntity<>(HttpStatus.UNAUTHORIZED); + } + if (activity instanceof Undo) { + return new ResponseEntity<>(HttpStatus.OK); + } + return new ResponseEntity<>(HttpStatus.BAD_REQUEST); + } } diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/Activity.java b/juick-server/src/main/java/com/juick/server/api/activity/model/Activity.java new file mode 100644 index 00000000..ec126b88 --- /dev/null +++ b/juick-server/src/main/java/com/juick/server/api/activity/model/Activity.java @@ -0,0 +1,23 @@ +package com.juick.server.api.activity.model; + +public abstract class Activity extends Context { + + private String actor; + private Object object; + + public String getActor() { + return actor; + } + + public void setActor(String actor) { + this.actor = actor; + } + + public Object getObject() { + return object; + } + + public void setObject(Object object) { + this.object = object; + } +} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/ActivityObject.java b/juick-server/src/main/java/com/juick/server/api/activity/model/ActivityObject.java deleted file mode 100644 index fceb3612..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/ActivityObject.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.juick.server.api.activity.model; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.time.Instant; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -public abstract class ActivityObject { - - private List<String> context; - - private String id; - - private Instant published; - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getType() { - return getClass().getSimpleName(); - } - - @JsonProperty("@context") - public List<String> getContext() { - return context; - } - - public final static String ACTIVITY_STREAMS_URI = "https://www.w3.org/ns/activitystreams"; - public final static String SECURITY_URI = "https://w3id.org/security/v1"; - public final static String LD_JSON_MEDIA_TYPE = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""; - public final static String ACTIVITY_JSON_MEDIA_TYPE = "application/activity+json; profile=\"https://www.w3.org/ns/activitystreams\""; - - public Instant getPublished() { - return published; - } - - public void setPublished(Instant published) { - this.published = published; - } - - public static ActivityObject build(ActivityObject response) { - response.context = Arrays.asList(ACTIVITY_STREAMS_URI, SECURITY_URI); - return response; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/Context.java b/juick-server/src/main/java/com/juick/server/api/activity/model/Context.java new file mode 100644 index 00000000..984eb2cd --- /dev/null +++ b/juick-server/src/main/java/com/juick/server/api/activity/model/Context.java @@ -0,0 +1,82 @@ +package com.juick.server.api.activity.model; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.juick.server.api.activity.model.activities.*; + +import java.time.Instant; +import java.util.Arrays; +import java.util.List; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property="type") +@JsonSubTypes({ + @JsonSubTypes.Type(value = Create.class, name = "Create"), + @JsonSubTypes.Type(value = Delete.class, name = "Delete"), + @JsonSubTypes.Type(value = Follow.class, name = "Follow"), + @JsonSubTypes.Type(value = Accept.class, name = "Accept"), + @JsonSubTypes.Type(value = Undo.class, name = "Undo"), + @JsonSubTypes.Type(value = Image.class, name = "Image"), + @JsonSubTypes.Type(value = Key.class, name = "Key"), + @JsonSubTypes.Type(value = Link.class, name = "Link"), + @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") +}) +public abstract class Context { + + private List<Object> context; + + private String id; + + private Instant published; + + + private List<String> to; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return getClass().getSimpleName(); + } + + @JsonProperty("@context") + public List<Object> getContext() { + return context; + } + + public final static String ACTIVITY_STREAMS_URI = "https://www.w3.org/ns/activitystreams"; + public final static String SECURITY_URI = "https://w3id.org/security/v1"; + public final static String LD_JSON_MEDIA_TYPE = "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\""; + public final static String ACTIVITY_JSON_MEDIA_TYPE = "application/activity+json; profile=\"https://www.w3.org/ns/activitystreams\""; + + public Instant getPublished() { + return published; + } + + public void setPublished(Instant published) { + this.published = published; + } + + public List<String> getTo() { + return to; + } + + public void setTo(List<String> to) { + this.to = to; + } + + public static Context build(Context response) { + response.context = Arrays.asList(ACTIVITY_STREAMS_URI, SECURITY_URI); + return response; + } +} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/Create.java b/juick-server/src/main/java/com/juick/server/api/activity/model/Create.java deleted file mode 100644 index 2acdd6a6..00000000 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/Create.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.juick.server.api.activity.model; - -import java.util.List; - -public class Create extends ActivityObject { - - private String actor; - private Note object; - private List<String> to; - - public String getActor() { - return actor; - } - - public void setActor(String actor) { - this.actor = actor; - } - - public Note getObject() { - return object; - } - - public void setObject(Note object) { - this.object = object; - } - - public List<String> getTo() { - return to; - } - - public void setTo(List<String> to) { - this.to = to; - } -} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/Image.java b/juick-server/src/main/java/com/juick/server/api/activity/model/Image.java index 9a3b1659..c8f869a5 100644 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/Image.java +++ b/juick-server/src/main/java/com/juick/server/api/activity/model/Image.java @@ -1,6 +1,6 @@ package com.juick.server.api.activity.model; -public class Image extends ActivityObject { +public class Image extends Context { private String mediaType; private String url; diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/Key.java b/juick-server/src/main/java/com/juick/server/api/activity/model/Key.java index 32417778..bc41b460 100644 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/Key.java +++ b/juick-server/src/main/java/com/juick/server/api/activity/model/Key.java @@ -1,6 +1,6 @@ package com.juick.server.api.activity.model; -public class Key extends ActivityObject { +public class Key extends Context { private String owner; private String publicKeyPem; diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/Link.java b/juick-server/src/main/java/com/juick/server/api/activity/model/Link.java index b57dabbe..543b5f0c 100644 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/Link.java +++ b/juick-server/src/main/java/com/juick/server/api/activity/model/Link.java @@ -1,6 +1,6 @@ package com.juick.server.api.activity.model; -public class Link extends ActivityObject { +public class Link extends Context { private String href; public String getHref() { diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/Note.java b/juick-server/src/main/java/com/juick/server/api/activity/model/Note.java index ac58b033..ff64c4b9 100644 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/Note.java +++ b/juick-server/src/main/java/com/juick/server/api/activity/model/Note.java @@ -2,7 +2,7 @@ package com.juick.server.api.activity.model; import java.util.List; -public class Note extends ActivityObject { +public class Note extends Context { private String content; private String attributedTo; private Link attachment; diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/OrderedCollection.java b/juick-server/src/main/java/com/juick/server/api/activity/model/OrderedCollection.java index 90f04de3..d66c55be 100644 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/OrderedCollection.java +++ b/juick-server/src/main/java/com/juick/server/api/activity/model/OrderedCollection.java @@ -1,6 +1,6 @@ package com.juick.server.api.activity.model; -public class OrderedCollection extends ActivityObject { +public class OrderedCollection extends Context { private int totalItems; diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/OrderedCollectionPage.java b/juick-server/src/main/java/com/juick/server/api/activity/model/OrderedCollectionPage.java index af7f2cec..bcae87d0 100644 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/OrderedCollectionPage.java +++ b/juick-server/src/main/java/com/juick/server/api/activity/model/OrderedCollectionPage.java @@ -2,7 +2,7 @@ package com.juick.server.api.activity.model; import java.util.List; -public class OrderedCollectionPage extends ActivityObject { +public class OrderedCollectionPage extends Context { private String partOf; @@ -12,7 +12,7 @@ public class OrderedCollectionPage extends ActivityObject { private String last; - private List<? extends ActivityObject> orderedItems; + private List<? extends Context> orderedItems; public String getNext() { return next; @@ -22,11 +22,11 @@ public class OrderedCollectionPage extends ActivityObject { this.next = next; } - public List<? extends ActivityObject> getOrderedItems() { + public List<? extends Context> getOrderedItems() { return orderedItems; } - public void setOrderedItems(List<? extends ActivityObject> orderedItems) { + public void setOrderedItems(List<? extends Context> orderedItems) { this.orderedItems = orderedItems; } diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/Person.java b/juick-server/src/main/java/com/juick/server/api/activity/model/Person.java index e314624d..8a817c18 100644 --- a/juick-server/src/main/java/com/juick/server/api/activity/model/Person.java +++ b/juick-server/src/main/java/com/juick/server/api/activity/model/Person.java @@ -1,6 +1,8 @@ package com.juick.server.api.activity.model; -public class Person extends ActivityObject { +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +public class Person extends Context { private String name; private String preferredUsername; @@ -25,6 +27,7 @@ public class Person extends ActivityObject { this.name = name; } + @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) public Image getIcon() { return icon; } @@ -73,6 +76,7 @@ public class Person extends ActivityObject { this.url = url; } + @JsonTypeInfo(use = JsonTypeInfo.Id.NONE) public Key getPublicKey() { return publicKey; } diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Accept.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Accept.java new file mode 100644 index 00000000..1e0a9968 --- /dev/null +++ b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Accept.java @@ -0,0 +1,6 @@ +package com.juick.server.api.activity.model.activities; + +import com.juick.server.api.activity.model.Activity; + +public class Accept extends Activity { +} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Create.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Create.java new file mode 100644 index 00000000..52507373 --- /dev/null +++ b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Create.java @@ -0,0 +1,6 @@ +package com.juick.server.api.activity.model.activities; + +import com.juick.server.api.activity.model.Activity; + +public class Create extends Activity { +} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Delete.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Delete.java new file mode 100644 index 00000000..f4392020 --- /dev/null +++ b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Delete.java @@ -0,0 +1,6 @@ +package com.juick.server.api.activity.model.activities; + +import com.juick.server.api.activity.model.Activity; + +public class Delete extends Activity { +} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Follow.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Follow.java new file mode 100644 index 00000000..573ecc6e --- /dev/null +++ b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Follow.java @@ -0,0 +1,6 @@ +package com.juick.server.api.activity.model.activities; + +import com.juick.server.api.activity.model.Activity; + +public class Follow extends Activity { +} diff --git a/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Undo.java b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Undo.java new file mode 100644 index 00000000..4e87e9d0 --- /dev/null +++ b/juick-server/src/main/java/com/juick/server/api/activity/model/activities/Undo.java @@ -0,0 +1,6 @@ +package com.juick.server.api.activity.model.activities; + +import com.juick.server.api.activity.model.Activity; + +public class Undo extends Activity { +} |