diff options
7 files changed, 137 insertions, 83 deletions
diff --git a/src/main/java/com/juick/ActivityPubManager.java b/src/main/java/com/juick/ActivityPubManager.java index c909bd9b..16f310a7 100644 --- a/src/main/java/com/juick/ActivityPubManager.java +++ b/src/main/java/com/juick/ActivityPubManager.java @@ -59,6 +59,7 @@ import com.juick.util.HttpUtils; import com.juick.util.MessageUtils; import com.juick.util.formatters.PlainTextFormatter; import com.juick.www.api.SystemActivity.ActivityType; +import com.juick.www.api.activity.helpers.ProfileUriBuilder; import com.juick.www.api.activity.model.Context; import com.juick.www.api.activity.model.activities.Accept; import com.juick.www.api.activity.model.activities.Announce; @@ -76,7 +77,6 @@ import io.pebbletemplates.pebble.template.PebbleTemplate; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; @@ -90,8 +90,8 @@ public class ActivityPubManager implements ActivityListener, NotificationListene private MessagesService messagesService; @Inject private PebbleEngine pebbleEngine; - @Value("${ap_base_uri:http://localhost:8080/}") - private String baseUri; + @Inject + ProfileUriBuilder profileUriBuilder; @Override public void processFollowEvent(@Nonnull FollowEvent followEvent) { @@ -137,7 +137,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene public void deleteMessageEvent(DeleteMessageEvent event) { Message msg = event.getMessage(); User user = msg.getUser(); - String userUri = personUri(user); + String userUri = profileUriBuilder.personUri(user); Note note = makeNote(msg); Actor me = (Actor) signatureManager.getContext(URI.create(userUri)).get(); socialService.getFollowers(user).forEach(acct -> { @@ -190,7 +190,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene public void processUpdateEvent(UpdateEvent event) { String objectUri = event.getMessageUri(); User user = event.getUser(); - String userUri = personUri(user); + String userUri = profileUriBuilder.personUri(user); Actor me = (Actor) signatureManager.getContext(URI.create(userUri)).get(); socialService.getFollowers(user).forEach(acct -> { try { @@ -222,7 +222,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene @Override public void processUpdateUserEvent(UpdateUserEvent event) { User user = event.getUser(); - String userUri = personUri(user); + String userUri = profileUriBuilder.personUri(user); Actor me = (Actor) signatureManager.getContext(URI.create(userUri)).get(); socialService.getFollowers(user).forEach(acct -> { try { @@ -246,7 +246,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene return; } User user = msg.getUser(); - String userUri = personUri(user); + String userUri = profileUriBuilder.personUri(user); Note note = makeNote(msg); signatureManager.getContext(URI.create(userUri)).ifPresentOrElse((me) -> { Set<String> subscribers = new HashSet<>(socialService.getFollowers(user)); @@ -281,72 +281,23 @@ public class ActivityPubManager implements ActivityListener, NotificationListene }); } - public String inboxUri() { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath("/api/inbox").toUriString(); - } - - public String outboxUri(User user) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/u/%s/blog/toc", user.getName())).toUriString(); - } - - public String personUri(User user) { - if (user.getUri().toString().length() > 0) { - return user.getUri().toASCIIString(); - } - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/u/%s", user.getName())).toUriString(); - } - public String personWebUri(User user) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/%s/", user.getName())).toUriString(); - } - - public String followersUri(User user) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/u/%s/followers/toc", user.getName())).toUriString(); - } - - public String followingUri(User user) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/u/%s/following/toc", user.getName())).toUriString(); - } - public String messageUri(Message msg) { - return messageUri(msg.getMid(), msg.getRid()); - } - public String messageUri(int mid, int rid) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - uri.replacePath(String.format("/n/%d-%d", mid, rid)); - return uri.toUriString(); - } - public String tagUri(Tag tag) { - UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); - return uri.replacePath(String.format("/t/%s", tag.getName())).toUriString(); - } - - public String postId(String messageUri) { - UriComponents uri = UriComponentsBuilder.fromUriString(messageUri).build(); - return uri.getPath().substring(uri.getPath().lastIndexOf('/') + 1).replace("-", "/"); - } - public Note makeNote(Message msg) { Note note = new Note(); - note.setId(messageUri(msg)); + note.setId(profileUriBuilder.messageUri(msg)); note.setUrl(PlainTextFormatter.formatUrl(msg)); - note.setAttributedTo(personUri(msg.getUser())); + note.setAttributedTo(profileUriBuilder.personUri(msg.getUser())); if (MessageUtils.isReply(msg)) { if (msg.getReplyToUri().toASCIIString().length() > 0) { note.setInReplyTo(msg.getReplyToUri().toASCIIString()); } else { - note.setInReplyTo(messageUri(msg.getMid(), msg.getReplyto())); + note.setInReplyTo(profileUriBuilder.messageUri(msg.getMid(), msg.getReplyto())); } } if (MessageUtils.isPM(msg)) { - note.setTo(Collections.singletonList(personUri(msg.getTo()))); + note.setTo(Collections.singletonList(profileUriBuilder.personUri(msg.getTo()))); } else { note.setTo(Collections.singletonList(Context.ACTIVITYSTREAMS_PUBLIC)); - note.setCc(Collections.singletonList(followersUri(msg.getUser()))); + note.setCc(Collections.singletonList(profileUriBuilder.followersUri(msg.getUser()))); } note.setPublished(msg.getCreated()); if (StringUtils.isNotBlank(msg.getAttachmentType())) { @@ -356,7 +307,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene attachment.setMediaType(HttpUtils.mediaType(msg.getAttachmentType())); note.setAttachment(Collections.singletonList(attachment)); } - note.setTags(msg.getTags().stream().map(t -> new Hashtag(tagUri(t), t.getName())).collect(Collectors.toList())); + note.setTags(msg.getTags().stream().map(t -> new Hashtag(profileUriBuilder.tagUri(t), t.getName())).collect(Collectors.toList())); if (msg.getReplyToUri() != null && msg.getReplyToUri().toASCIIString().length() > 0) { Optional<Context> noteContext = signatureManager.getContext(msg.getReplyToUri()); if (noteContext.isPresent()) { @@ -370,7 +321,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene } } } else if (MessageUtils.isReply(msg)) { - note.getTags().add(new Mention(personWebUri(msg.getTo()), msg.getTo().getName())); + note.getTags().add(new Mention(profileUriBuilder.personWebUri(msg.getTo()), msg.getTo().getName())); } MessageUtils.getGlobalMentions(msg).forEach(m -> { // @user@server.tld -> user@server.tld @@ -390,7 +341,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene PebbleTemplate noteTemplate = pebbleEngine.getTemplate("layouts/note"); Map<String, Object> context = new HashMap<>(); context.put("msg", msg); - context.put("baseUri", baseUri); + context.put("baseUri", profileUriBuilder.getBaseUri()); try { Writer writer = new StringWriter(); noteTemplate.evaluate(writer, context); @@ -412,7 +363,7 @@ public class ActivityPubManager implements ActivityListener, NotificationListene Note note = makeNote(message); Announce announce = new Announce(); announce.setId(note.getId() + "#announce-" + user.getName()); - announce.setActor(personUri(user)); + announce.setActor(profileUriBuilder.personUri(user)); announce.setTo(Collections.singletonList(Context.ACTIVITYSTREAMS_PUBLIC)); announce.setObject(note); signatureManager.getContext(URI.create(announce.getActor())).ifPresentOrElse((ctx) -> { diff --git a/src/main/java/com/juick/config/ActivityPubConfig.java b/src/main/java/com/juick/config/ActivityPubConfig.java index 786e19c4..3570b97d 100644 --- a/src/main/java/com/juick/config/ActivityPubConfig.java +++ b/src/main/java/com/juick/config/ActivityPubConfig.java @@ -24,7 +24,9 @@ import com.juick.util.ActivityPubRequestInterceptor; import com.juick.www.WebApp; import com.juick.www.api.activity.converters.UserToActorConverter; +import com.juick.www.api.activity.helpers.ProfileUriBuilder; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; @@ -45,6 +47,8 @@ public class ActivityPubConfig { KeystoreManager keystoreManager; @Inject WebApp webApp; + @Value("${ap_base_uri:http://localhost:8080/}") + private String baseUri; @Bean MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() { @@ -80,6 +84,10 @@ public class ActivityPubConfig { @Bean UserToActorConverter userToActorConverter() { - return new UserToActorConverter(activityPubManager(), keystoreManager, webApp); + return new UserToActorConverter(profileUriBuilder(), keystoreManager, webApp); + } + @Bean + ProfileUriBuilder profileUriBuilder() { + return new ProfileUriBuilder(baseUri); } } diff --git a/src/main/java/com/juick/www/api/Post.java b/src/main/java/com/juick/www/api/Post.java index 9faf883a..63c95f2a 100644 --- a/src/main/java/com/juick/www/api/Post.java +++ b/src/main/java/com/juick/www/api/Post.java @@ -23,9 +23,10 @@ import java.util.List; import java.util.Optional; import javax.inject.Inject; + +import com.juick.www.api.activity.helpers.ProfileUriBuilder; import jakarta.validation.constraints.NotNull; -import com.juick.ActivityPubManager; import com.juick.CommandsManager; import com.juick.model.CommandResult; import com.juick.model.Message; @@ -75,7 +76,7 @@ public class Post { @Inject ApplicationEventPublisher applicationEventPublisher; @Inject - ActivityPubManager activityPubManager; + ProfileUriBuilder profileUriBuilder; @RequestMapping(value = "/api/post", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(value = HttpStatus.OK) @@ -236,7 +237,7 @@ public class Post { messagesService.getMessage(mid).orElseThrow(IllegalStateException::new) : messagesService.getReply(mid, rid); applicationEventPublisher.publishEvent( - new UpdateEvent(this, author, activityPubManager.messageUri(mid, rid))); + new UpdateEvent(this, author, profileUriBuilder.messageUri(mid, rid))); return CommandResult.build(result, "Message updated", StringUtils.EMPTY); } throw new HttpBadRequestException(); diff --git a/src/main/java/com/juick/www/api/activity/Profile.java b/src/main/java/com/juick/www/api/activity/Profile.java index ff1227a3..5deba2cf 100644 --- a/src/main/java/com/juick/www/api/activity/Profile.java +++ b/src/main/java/com/juick/www/api/activity/Profile.java @@ -24,6 +24,7 @@ import com.juick.util.formatters.PlainTextFormatter; import com.juick.model.CommandResult; import com.juick.ActivityPubManager; import com.juick.CommandsManager; +import com.juick.www.api.activity.helpers.ProfileUriBuilder; import com.juick.www.api.activity.model.Activity; import com.juick.www.api.activity.model.Context; import com.juick.www.api.activity.model.activities.Announce; @@ -85,6 +86,8 @@ public class Profile { @Inject private ActivityPubManager activityPubManager; @Inject + private ProfileUriBuilder profileUriBuilder; + @Inject private ApplicationEventPublisher applicationEventPublisher; @Inject private CommandsManager commandsManager; @@ -195,7 +198,7 @@ public class Profile { Person follower = new Person(); follower.setName(a.getName()); follower.setPreferredUsername(a.getName()); - follower.setUrl(activityPubManager.personWebUri(a)); + follower.setUrl(profileUriBuilder.personWebUri(a)); return follower; }).collect(Collectors.toList())); boolean hasNext = followers.size() <= 20 * page; @@ -239,7 +242,7 @@ public class Profile { Person follower = new Person(); follower.setName(a.getName()); follower.setPreferredUsername(a.getName()); - follower.setUrl(activityPubManager.personWebUri(a)); + follower.setUrl(profileUriBuilder.personWebUri(a)); return follower; }).collect(Collectors.toList())); boolean hasNext = following.size() <= 20 * page; @@ -301,7 +304,7 @@ public class Profile { String inReplyTo = (String) note.getInReplyTo(); if (StringUtils.isNotBlank(inReplyTo)) { if (inReplyTo.startsWith(baseUri)) { - String postId = activityPubManager.postId(inReplyTo); + String postId = profileUriBuilder.postId(inReplyTo); User user = new User(); user.setUri(URI.create(activity.getActor())); String markdown = remarkConverter.convertFragment((String) note.getContent()); diff --git a/src/main/java/com/juick/www/api/activity/converters/UserToActorConverter.java b/src/main/java/com/juick/www/api/activity/converters/UserToActorConverter.java index 405c27ab..435cb50d 100644 --- a/src/main/java/com/juick/www/api/activity/converters/UserToActorConverter.java +++ b/src/main/java/com/juick/www/api/activity/converters/UserToActorConverter.java @@ -1,8 +1,8 @@ package com.juick.www.api.activity.converters; +import com.juick.www.api.activity.helpers.ProfileUriBuilder; import org.springframework.core.convert.converter.Converter; -import com.juick.ActivityPubManager; import com.juick.KeystoreManager; import com.juick.model.User; import com.juick.www.WebApp; @@ -18,15 +18,15 @@ import lombok.AllArgsConstructor; @AllArgsConstructor public class UserToActorConverter implements Converter<User, Actor> { - private ActivityPubManager activityPubManager; + private ProfileUriBuilder profileUriBuilder; private KeystoreManager keystoreManager; private WebApp webApp; @Override public Actor convert(User user) { Actor profile = user.isService() ? new Application() : new Person(); - profile.setId(activityPubManager.personUri(user)); - profile.setUrl(activityPubManager.personWebUri(user)); + profile.setId(profileUriBuilder.personUri(user)); + profile.setUrl(profileUriBuilder.personWebUri(user)); profile.setName(user.getName()); profile.setPreferredUsername(user.getName()); Key publicKey = new Key(); @@ -34,10 +34,10 @@ public class UserToActorConverter implements Converter<User, Actor> { publicKey.setOwner(profile.getId()); publicKey.setPublicKeyPem(keystoreManager.getPublicKeyPem()); profile.setPublicKey(publicKey); - profile.setInbox(activityPubManager.inboxUri()); - profile.setOutbox(activityPubManager.outboxUri(user)); - profile.setFollowers(activityPubManager.followersUri(user)); - profile.setFollowing(activityPubManager.followingUri(user)); + profile.setInbox(profileUriBuilder.inboxUri()); + profile.setOutbox(profileUriBuilder.outboxUri(user)); + profile.setFollowers(profileUriBuilder.followersUri(user)); + profile.setFollowing(profileUriBuilder.followingUri(user)); Image avatar = new Image(); avatar.setUrl(webApp.getAvatarUrl(user)); avatar.setMediaType("image/png"); diff --git a/src/main/java/com/juick/www/api/activity/helpers/ProfileUriBuilder.java b/src/main/java/com/juick/www/api/activity/helpers/ProfileUriBuilder.java new file mode 100644 index 00000000..8f5e7566 --- /dev/null +++ b/src/main/java/com/juick/www/api/activity/helpers/ProfileUriBuilder.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2008-2022, 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.helpers; + +import com.juick.model.Message; +import com.juick.model.Tag; +import com.juick.model.User; +import lombok.AllArgsConstructor; +import org.springframework.web.util.UriComponents; +import org.springframework.web.util.UriComponentsBuilder; + +@AllArgsConstructor +public class ProfileUriBuilder { + private String baseUri; + + public String getBaseUri() { + return baseUri; + } + + public String inboxUri() { + UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); + return uri.replacePath("/api/inbox").toUriString(); + } + + public String outboxUri(User user) { + UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); + return uri.replacePath(String.format("/u/%s/blog/toc", user.getName())).toUriString(); + } + + public String personUri(User user) { + if (user.getUri().toString().length() > 0) { + return user.getUri().toASCIIString(); + } + UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); + return uri.replacePath(String.format("/u/%s", user.getName())).toUriString(); + } + + public String personWebUri(User user) { + UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); + return uri.replacePath(String.format("/%s/", user.getName())).toUriString(); + } + + public String followersUri(User user) { + UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); + return uri.replacePath(String.format("/u/%s/followers/toc", user.getName())).toUriString(); + } + + public String followingUri(User user) { + UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); + return uri.replacePath(String.format("/u/%s/following/toc", user.getName())).toUriString(); + } + + public String messageUri(Message msg) { + return messageUri(msg.getMid(), msg.getRid()); + } + + public String messageUri(int mid, int rid) { + UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); + uri.replacePath(String.format("/n/%d-%d", mid, rid)); + return uri.toUriString(); + } + + public String tagUri(Tag tag) { + UriComponentsBuilder uri = UriComponentsBuilder.fromUriString(baseUri); + return uri.replacePath(String.format("/t/%s", tag.getName())).toUriString(); + } + + public String postId(String messageUri) { + UriComponents uri = UriComponentsBuilder.fromUriString(messageUri).build(); + return uri.getPath().substring(uri.getPath().lastIndexOf('/') + 1) + .replace("-", "/"); + } +} diff --git a/src/test/java/com/juick/server/tests/ServerTests.java b/src/test/java/com/juick/server/tests/ServerTests.java index 224743a7..796417c8 100644 --- a/src/test/java/com/juick/server/tests/ServerTests.java +++ b/src/test/java/com/juick/server/tests/ServerTests.java @@ -152,6 +152,7 @@ import com.juick.www.ad.models.Site; import com.juick.www.api.SystemActivity; import com.juick.www.api.Users; import com.juick.www.api.activity.Profile; +import com.juick.www.api.activity.helpers.ProfileUriBuilder; import com.juick.www.api.activity.model.Context; import com.juick.www.api.activity.model.activities.Announce; import com.juick.www.api.activity.model.activities.Create; @@ -289,6 +290,8 @@ public class ServerTests { @Inject private ActivityPubManager activityPubManager; @Inject + private ProfileUriBuilder profileUriBuilder; + @Inject private WebApp webApp; @Inject private RestTemplate apClient; @@ -1485,7 +1488,7 @@ public class ServerTests { Mockito.verify(activityListener, Mockito.times(1)).onApplicationEvent(updateEventCaptor.capture()); UpdateEvent updateEvent = updateEventCaptor.getValue(); assertThat(updateEvent.getUser(), is(ugnich)); - assertThat(activityPubManager.messageUri(original.getMid(), 0), is(updateEvent.getMessageUri())); + assertThat(profileUriBuilder.messageUri(original.getMid(), 0), is(updateEvent.getMessageUri())); mockMvc.perform(post("/api/update").with(httpBasic(freefdName, freefdPassword)) .param("mid", String.valueOf(original.getMid())).param("body", "PEOPLE")) .andExpect(status().is(403)); @@ -2124,9 +2127,9 @@ public class ServerTests { json = jsonMapper.writeValueAsString(Context.build(note)); Note replyNote = new Note(); replyNote.setId("http://localhost:8080/n/2-1"); - replyNote.setInReplyTo(activityPubManager.messageUri(msg)); + replyNote.setInReplyTo(profileUriBuilder.messageUri(msg)); replyNote.setAttributedTo("http://localhost:8080/u/freefd"); - replyNote.setTo(Collections.singletonList(activityPubManager.personUri(ugnich))); + replyNote.setTo(Collections.singletonList(profileUriBuilder.personUri(ugnich))); replyNote.setContent("HI"); Create create = new Create(); create.setId(replyNote.getId()); |