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 --- src/main/java/com/juick/SignatureManager.java | 54 ++++++++++++---------- .../java/com/juick/www/api/activity/Profile.java | 2 +- .../www/api/activity/model/objects/Actor.java | 10 ++++ 3 files changed, 40 insertions(+), 26 deletions(-) (limited to 'src/main/java/com') diff --git a/src/main/java/com/juick/SignatureManager.java b/src/main/java/com/juick/SignatureManager.java index 668669f1..e9cb4f6a 100644 --- a/src/main/java/com/juick/SignatureManager.java +++ b/src/main/java/com/juick/SignatureManager.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.juick.model.AnonymousUser; import com.juick.model.User; import com.juick.service.UserService; +import com.juick.service.activities.DeleteUserEvent; import com.juick.util.DateFormattersHolder; import com.juick.www.api.activity.model.Context; import com.juick.www.api.activity.model.objects.Person; @@ -30,6 +31,7 @@ import com.juick.www.api.webfinger.model.Link; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -69,6 +71,8 @@ public class SignatureManager { private UserService userService; @Inject private RestTemplate apClient; + @Inject + private ApplicationEventPublisher applicationEventPublisher; public void post(Person from, Person to, Context data) throws IOException, NoSuchAlgorithmException { UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromUriString(to.getInbox()); @@ -94,21 +98,17 @@ public class SignatureManager { logger.info("Remote response: {}", response.getStatusCodeValue()); } - public String addSignature(Actor 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(Actor 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(Actor from, String host, String method, - String path, String dateString, String digestHeader, - KeystoreManager keystoreManager) throws IOException { - List requiredHeaders = StringUtils.isEmpty(digestHeader) ? - Arrays.asList("(request-target)", "host", "date") + public String addSignature(Actor from, String host, String method, String path, String dateString, + String digestHeader, KeystoreManager keystoreManager) throws IOException { + List requiredHeaders = StringUtils.isEmpty(digestHeader) + ? Arrays.asList("(request-target)", "host", "date") : Arrays.asList("(request-target)", "host", "date", "digest"); - Signature templateSignature = new Signature(from.getPublicKey().getId(), - "rsa-sha256", null, requiredHeaders); + Signature templateSignature = new Signature(from.getPublicKey().getId(), "rsa-sha256", null, requiredHeaders); Map headers = new HashMap<>(); headers.put("host", host); headers.put("date", dateString); @@ -125,11 +125,11 @@ public class SignatureManager { String signatureString = headers.get("signature"); logger.info("Signature: {}", signatureString); Signature signature = Signature.fromString(signatureString); - Optional context = getContext(UriComponentsBuilder.fromUriString(signature.getKeyId()) - .fragment(null).build().toUri()); + Optional context = getContext( + UriComponentsBuilder.fromUriString(signature.getKeyId()).fragment(null).build().toUri()); if (context.isPresent() && context.get() instanceof Actor) { - Actor securityObject = (Actor) context.get(); - Key key = KeystoreManager.publicKeyOf(securityObject); + Actor actor = (Actor) context.get(); + Key key = KeystoreManager.publicKeyOf(actor); Verifier verifier = new Verifier(key, signature); try { @@ -137,22 +137,27 @@ public class SignatureManager { logger.info("signature of {} is valid: {}", signature.getKeyId(), result); if (result) { User user = new User(); - user.setUri(URI.create(securityObject.getId())); + user.setUri(URI.create(actor.getId())); if (key.equals(keystoreManager.getPublicKey())) { - return userService.getUserByName(securityObject.getName()); + return userService.getUserByName(actor.getName()); + } + if (actor.isSuspended()) { + logger.info("{} is suspended, deleting", actor.getId()); + applicationEventPublisher.publishEvent(new DeleteUserEvent(this, actor.getId())); } return user; } else { return AnonymousUser.INSTANCE; } } catch (NoSuchAlgorithmException | SignatureException | MissingRequiredHeaderException | IOException e) { - logger.warn("Invalid signature {}", signatureString); + logger.warn("Invalid signature {}: {}", signatureString, e.getMessage()); } } else { logger.warn("Unknown keyId"); } return AnonymousUser.INSTANCE; } + public Optional getContext(URI contextUri) { try { Context context = apClient.getForEntity(contextUri, Context.class).getBody(); @@ -166,17 +171,16 @@ public class SignatureManager { } return Optional.empty(); } + public Optional discoverPerson(String acct) { Jid acctId = Jid.of(acct); - URI resourceUri = UriComponentsBuilder.fromPath("/.well-known/webfinger") - .host(acctId.getDomain()) - .scheme("https") - .queryParam("resource", String.format("%s", acctId.toEscapedString())).build().toUri(); + URI resourceUri = UriComponentsBuilder.fromPath("/.well-known/webfinger").host(acctId.getDomain()) + .scheme("https").queryParam("resource", String.format("%s", acctId.toEscapedString())).build().toUri(); HttpHeaders headers = new HttpHeaders(); headers.add("Accept", "application/jrd+json"); HttpEntity webfingerRequest = new HttpEntity<>(headers); - ResponseEntity response = apClient.exchange( - resourceUri, HttpMethod.GET, webfingerRequest, Account.class); + ResponseEntity response = apClient.exchange(resourceUri, HttpMethod.GET, webfingerRequest, + Account.class); if (response.getStatusCode().is2xxSuccessful()) { Account acctData = response.getBody(); if (acctData != null) { 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 724e0747..cf5fb843 100644 --- a/src/main/java/com/juick/www/api/activity/Profile.java +++ b/src/main/java/com/juick/www/api/activity/Profile.java @@ -376,7 +376,7 @@ public class Profile { if (activity instanceof Delete) { // Delete gone user // TODO: check if it is really deleted and remove copy-paste - if (activity.getActor().equals(activity.getObject().getUrl())) { + if (activity.getActor().equals(activity.getObject().getId())) { return new ResponseEntity<>(CommandResult.fromString("Delete request accepted"), HttpStatus.ACCEPTED); } diff --git a/src/main/java/com/juick/www/api/activity/model/objects/Actor.java b/src/main/java/com/juick/www/api/activity/model/objects/Actor.java index f8bd63e0..7e799a95 100644 --- a/src/main/java/com/juick/www/api/activity/model/objects/Actor.java +++ b/src/main/java/com/juick/www/api/activity/model/objects/Actor.java @@ -23,6 +23,8 @@ import com.juick.www.api.activity.model.Context; public class Actor extends Context { private String preferredUsername; + private boolean suspended; + private String inbox; private String outbox; private String following; @@ -78,4 +80,12 @@ public class Actor extends Context { public void setPreferredUsername(String preferredUsername) { this.preferredUsername = preferredUsername; } + + public boolean isSuspended() { + return suspended; + } + + public void setSuspended(boolean suspended) { + this.suspended = suspended; + } } -- cgit v1.2.3