aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Vitaly Takmazov2023-11-11 23:15:01 +0300
committerGravatar Vitaly Takmazov2023-11-11 23:15:01 +0300
commit816db24929732fa3967667ec76d95cbfb01068d1 (patch)
tree6a1686bb4161be22ff32765ec270236935d94dcd /src
parent4d51f184da68cf8b70bdd406f0b90f3e758f1607 (diff)
Make sure HTTP requests actually use caching
* OkHttp dispatcher is not used for synchronous requests * @Cacheable only works for bean method calls, refactor beans to use it correctly
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/juick/ActivityPubManager.java58
-rw-r--r--src/main/java/com/juick/config/HttpClientConfig.java2
-rw-r--r--src/main/java/com/juick/config/SecurityConfig.java5
-rw-r--r--src/main/java/com/juick/service/ActivityPubService.java65
-rw-r--r--src/main/java/com/juick/service/security/HTTPSignatureAuthenticationFilter.java10
5 files changed, 66 insertions, 74 deletions
diff --git a/src/main/java/com/juick/ActivityPubManager.java b/src/main/java/com/juick/ActivityPubManager.java
index 566cc1fd..461f4731 100644
--- a/src/main/java/com/juick/ActivityPubManager.java
+++ b/src/main/java/com/juick/ActivityPubManager.java
@@ -19,6 +19,7 @@ package com.juick;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
+import com.juick.model.AnonymousUser;
import com.juick.model.Message;
import com.juick.model.Reaction;
import com.juick.model.User;
@@ -46,10 +47,14 @@ import io.pebbletemplates.pebble.template.PebbleTemplate;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.convert.ConversionService;
import org.springframework.http.MediaType;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
+import org.tomitribe.auth.signatures.MissingRequiredHeaderException;
+import org.tomitribe.auth.signatures.Signature;
+import org.tomitribe.auth.signatures.Verifier;
import javax.annotation.Nonnull;
import javax.inject.Inject;
@@ -57,7 +62,9 @@ import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.net.URI;
+import java.security.Key;
import java.security.NoSuchAlgorithmException;
+import java.security.SignatureException;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;
@@ -80,6 +87,10 @@ public class ActivityPubManager implements ActivityListener, NotificationListene
ConversionService conversionService;
@Inject
ObjectMapper jsonMapper;
+ @Inject
+ private ApplicationEventPublisher applicationEventPublisher;
+ @Inject
+ private KeystoreManager keystoreManager;
@Override
public void processFollowEvent(@Nonnull FollowEvent followEvent) {
@@ -420,4 +431,51 @@ public class ActivityPubManager implements ActivityListener, NotificationListene
throw new HttpNotFoundException();
}
}
+
+ public User verifyActor(String method, String path, Map<String, String> headers) {
+ String signatureString = headers.get("signature");
+ if (StringUtils.isNotEmpty(signatureString)) {
+ Signature signature = Signature.fromString(signatureString);
+ var keyId = UriComponentsBuilder.fromUriString(signature.getKeyId()).fragment(null).build().toUri();
+ var user = activityPubService.getUserByAccountUri(keyId.toASCIIString());
+ Key key = null;
+ Actor actor = null;
+ if (!user.isAnonymous()) {
+ // local user
+ key = keystoreManager.getPublicKey();
+ } else {
+ var context = activityPubService.get(keyId);
+ if (context.isPresent()) {
+ actor = (Actor) context.get();
+ key = KeystoreManager.publicKeyOf(actor);
+ }
+ }
+ if (key != null) {
+ Verifier verifier = new Verifier(key, signature);
+ try {
+ boolean result = verifier.verify(method.toLowerCase(), path, headers);
+ if (result) {
+ if (!user.isAnonymous()) {
+ return user;
+ } else {
+ if (actor != null) {
+ User person = new User();
+ person.setUri(URI.create(actor.getId()));
+ if (actor.isSuspended()) {
+ logger.info("{} is suspended, deleting", actor.getId());
+ applicationEventPublisher
+ .publishEvent(new DeleteUserEvent(this, actor.getId()));
+ }
+ return person;
+ }
+ }
+ }
+ } catch (NoSuchAlgorithmException | SignatureException | MissingRequiredHeaderException
+ | IOException e) {
+ logger.warn("Verification error for {}: {}", signature.getKeyId(), e.getMessage());
+ }
+ }
+ }
+ return AnonymousUser.INSTANCE;
+ }
}
diff --git a/src/main/java/com/juick/config/HttpClientConfig.java b/src/main/java/com/juick/config/HttpClientConfig.java
index 2540f4db..d838575d 100644
--- a/src/main/java/com/juick/config/HttpClientConfig.java
+++ b/src/main/java/com/juick/config/HttpClientConfig.java
@@ -28,7 +28,6 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.inject.Inject;
-import java.util.concurrent.Executors;
@Configuration
public class HttpClientConfig {
@@ -38,7 +37,6 @@ public class HttpClientConfig {
@Bean
public OkHttpClient httpClient() {
return new OkHttpClient.Builder()
- .dispatcher(new Dispatcher(Executors.newFixedThreadPool(1)))
.addInterceptor(new HttpLoggingInterceptor(logger::debug)
.setLevel(HttpLoggingInterceptor.Level.BASIC))
.addInterceptor(new ActivityPubRequestInterceptor())
diff --git a/src/main/java/com/juick/config/SecurityConfig.java b/src/main/java/com/juick/config/SecurityConfig.java
index e02e32ed..c557ab4e 100644
--- a/src/main/java/com/juick/config/SecurityConfig.java
+++ b/src/main/java/com/juick/config/SecurityConfig.java
@@ -17,6 +17,7 @@
package com.juick.config;
+import com.juick.ActivityPubManager;
import com.juick.KeystoreManager;
import com.juick.service.ActivityPubService;
import com.juick.service.UserService;
@@ -105,7 +106,7 @@ public class SecurityConfig {
}
@Inject
- private ActivityPubService activityPubService;
+ private ActivityPubManager activityPubManager;
@Bean
HashParamAuthenticationFilter apiAuthenticationFilter() {
@@ -190,7 +191,7 @@ public class SecurityConfig {
SecurityFilterChain apiChain(HttpSecurity http) throws Exception {
http.securityMatcher("/api/**", "/u/**", "/n/**")
.addFilterBefore(apiAuthenticationFilter(), BasicAuthenticationFilter.class)
- .addFilterBefore(new HTTPSignatureAuthenticationFilter(activityPubService, userService),
+ .addFilterBefore(new HTTPSignatureAuthenticationFilter(activityPubManager, userService),
BasicAuthenticationFilter.class)
.authorizeHttpRequests(requests -> requests
.requestMatchers(HttpMethod.OPTIONS).permitAll()
diff --git a/src/main/java/com/juick/service/ActivityPubService.java b/src/main/java/com/juick/service/ActivityPubService.java
index c2d3f1e7..f89f3261 100644
--- a/src/main/java/com/juick/service/ActivityPubService.java
+++ b/src/main/java/com/juick/service/ActivityPubService.java
@@ -18,10 +18,8 @@
package com.juick.service;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.juick.KeystoreManager;
import com.juick.model.AnonymousUser;
import com.juick.model.User;
-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.Actor;
@@ -29,12 +27,10 @@ import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
-import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.Cacheable;
-import org.springframework.context.ApplicationEventPublisher;
import org.springframework.core.convert.ConversionService;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpHeaders;
@@ -43,21 +39,15 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import org.tomitribe.auth.signatures.Base64;
-import org.tomitribe.auth.signatures.MissingRequiredHeaderException;
-import org.tomitribe.auth.signatures.Signature;
-import org.tomitribe.auth.signatures.Verifier;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import java.io.IOException;
import java.net.URI;
-import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
-import java.security.SignatureException;
import java.time.Instant;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
@Repository
@@ -76,10 +66,6 @@ public class ActivityPubService extends BaseJdbcService implements SocialService
@Inject
private SignatureService signatureService;
@Inject
- private ApplicationEventPublisher applicationEventPublisher;
- @Inject
- private KeystoreManager keystoreManager;
- @Inject
private User serviceUser;
@Inject
private ConversionService conversionService;
@@ -182,55 +168,4 @@ public class ActivityPubService extends BaseJdbcService implements SocialService
return response.code();
}
}
-
- public User verifyActor(String method, String path, Map<String, String> headers) {
- String signatureString = headers.get("signature");
- if (StringUtils.isNotEmpty(signatureString)) {
- try {
- Signature signature = Signature.fromString(signatureString);
- var keyId = UriComponentsBuilder.fromUriString(signature.getKeyId()).fragment(null).build().toUri();
- var user = getUserByAccountUri(keyId.toASCIIString());
- Key key = null;
- Actor actor = null;
- if (!user.isAnonymous()) {
- // local user
- key = keystoreManager.getPublicKey();
- } else {
- var context = get(keyId);
- if (context.isPresent()) {
- actor = (Actor) context.get();
- key = KeystoreManager.publicKeyOf(actor);
- }
- }
- if (key != null) {
- Verifier verifier = new Verifier(key, signature);
- try {
- boolean result = verifier.verify(method.toLowerCase(), path, headers);
- if (result) {
- if (!user.isAnonymous()) {
- return user;
- } else {
- if (actor != null) {
- User person = new User();
- person.setUri(URI.create(actor.getId()));
- if (actor.isSuspended()) {
- logger.info("{} is suspended, deleting", actor.getId());
- applicationEventPublisher
- .publishEvent(new DeleteUserEvent(this, actor.getId()));
- }
- return person;
- }
- }
- }
- } catch (NoSuchAlgorithmException | SignatureException | MissingRequiredHeaderException
- | IOException e) {
- logger.warn("Verification error for {}: {}", signature.getKeyId(), e.getMessage());
- }
- }
- } catch (Exception ex) {
-
- }
- }
- return AnonymousUser.INSTANCE;
- }
}
diff --git a/src/main/java/com/juick/service/security/HTTPSignatureAuthenticationFilter.java b/src/main/java/com/juick/service/security/HTTPSignatureAuthenticationFilter.java
index 55c87383..30cb1512 100644
--- a/src/main/java/com/juick/service/security/HTTPSignatureAuthenticationFilter.java
+++ b/src/main/java/com/juick/service/security/HTTPSignatureAuthenticationFilter.java
@@ -17,8 +17,8 @@
package com.juick.service.security;
+import com.juick.ActivityPubManager;
import com.juick.model.User;
-import com.juick.service.ActivityPubService;
import com.juick.service.UserService;
import com.juick.service.security.entities.JuickUser;
import jakarta.servlet.FilterChain;
@@ -37,13 +37,13 @@ import java.util.stream.Collectors;
public class HTTPSignatureAuthenticationFilter extends BaseAuthenticationFilter {
- private final ActivityPubService signatureManager;
+ private final ActivityPubManager activityPubManager;
private final UserService userService;
public HTTPSignatureAuthenticationFilter(
- final ActivityPubService activityPubService,
+ final ActivityPubManager activityPubManager,
final UserService userService) {
- this.signatureManager = activityPubService;
+ this.activityPubManager = activityPubManager;
this.userService = userService;
}
@@ -54,7 +54,7 @@ public class HTTPSignatureAuthenticationFilter extends BaseAuthenticationFilter
Map<String, String> headers = Collections.list(request.getHeaderNames())
.stream()
.collect(Collectors.toMap(String::toLowerCase, request::getHeader));
- var user = signatureManager.verifyActor(request.getMethod(), request.getRequestURI(), headers);
+ var user = activityPubManager.verifyActor(request.getMethod(), request.getRequestURI(), headers);
String userUri = user.getUri().toString();
if (!user.isAnonymous() || userUri.length() > 0) {
if (userUri.length() == 0) {