diff --git a/docs/openapi.yaml b/docs/openapi.yaml
index 5072eec..7c88c88 100644
--- a/docs/openapi.yaml
+++ b/docs/openapi.yaml
@@ -5,7 +5,7 @@ info:
license:
name: MIT
url: https://github.com/tiki/l0-storage/blob/main/LICENSE
- version: 0.0.12
+ version: 0.0.13
servers:
- url: https://storage.l0.mytiki.com
paths:
@@ -14,7 +14,7 @@ paths:
tags:
- STORAGE
summary: Upload a block
- operationId: post
+ operationId: put
requestBody:
content:
application/json:
@@ -43,7 +43,7 @@ paths:
tags:
- STORAGE
summary: Request a new token
- operationId: post_1
+ operationId: post
parameters:
- name: x-api-id
in: header
@@ -76,7 +76,7 @@ paths:
tags:
- STORAGE
summary: Submit a usage report
- operationId: post_2
+ operationId: post_1
requestBody:
content:
application/json:
diff --git a/pom.xml b/pom.xml
index acef26c..1764515 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,7 +12,7 @@
com.mytiki
l0_storage
- 0.0.12
+ 0.0.13
jar
L0 Storage
diff --git a/src/main/java/com/mytiki/l0_storage/features/latest/FeaturesConfig.java b/src/main/java/com/mytiki/l0_storage/features/latest/FeaturesConfig.java
index b0c2f0f..3f1ae59 100644
--- a/src/main/java/com/mytiki/l0_storage/features/latest/FeaturesConfig.java
+++ b/src/main/java/com/mytiki/l0_storage/features/latest/FeaturesConfig.java
@@ -6,8 +6,8 @@
package com.mytiki.l0_storage.features.latest;
import com.mytiki.l0_storage.features.latest.api_id.ApiIdConfig;
-import com.mytiki.l0_storage.features.latest.token.TokenConfig;
import com.mytiki.l0_storage.features.latest.report.ReportConfig;
+import com.mytiki.l0_storage.features.latest.token.TokenConfig;
import org.springframework.context.annotation.Import;
@Import({
diff --git a/src/main/java/com/mytiki/l0_storage/features/latest/token/TokenConfig.java b/src/main/java/com/mytiki/l0_storage/features/latest/token/TokenConfig.java
index 52f1184..1c6c328 100644
--- a/src/main/java/com/mytiki/l0_storage/features/latest/token/TokenConfig.java
+++ b/src/main/java/com/mytiki/l0_storage/features/latest/token/TokenConfig.java
@@ -26,6 +26,7 @@
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECPoint;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.Bean;
@@ -56,13 +57,13 @@ public TokenController tokenController(@Autowired TokenService service){
@Bean
public TokenService tokenService(
@Autowired TokenRepository repository,
- @Autowired JWSSigner signer,
+ @Autowired @Qualifier("tokenJwsSigner") JWSSigner signer,
@Autowired ApiIdService apiIdService,
@Value("${com.mytiki.l0_storage.token.exp}") long exp){
return new TokenService(repository, signer, apiIdService, exp);
}
- @Bean
+ @Bean("tokenJwkSet")
public JWKSet jwkSet(
@Value("${com.mytiki.l0_storage.token.private_key}") String pkcs8,
@Value("${com.mytiki.l0_storage.token.kid}") String kid)
@@ -76,7 +77,7 @@ public JWKSet jwkSet(
return new JWKSet(keyBuilder.build());
}
- @Bean
+ @Bean("tokenJwsSigner")
public JWSSigner jwsSigner(
@Autowired JWKSet jwkSet,
@Value("${com.mytiki.l0_storage.token.kid}") String kid)
@@ -84,7 +85,7 @@ public JWSSigner jwsSigner(
return new ECDSASigner(jwkSet.getKeyByKeyId(kid).toECKey().toECPrivateKey(), Curve.P_256);
}
- @Bean
+ @Bean("tokenJwtDecoder")
public JwtDecoder jwtDecoder(@Autowired JWKSet jwkSet) {
DefaultJWTProcessor jwtProcessor = new DefaultJWTProcessor<>();
ImmutableJWKSet immutableJWKSet = new ImmutableJWKSet<>(jwkSet);
diff --git a/src/main/java/com/mytiki/l0_storage/main/AppConfig.java b/src/main/java/com/mytiki/l0_storage/main/AppConfig.java
index 5e0068e..cee204d 100644
--- a/src/main/java/com/mytiki/l0_storage/main/AppConfig.java
+++ b/src/main/java/com/mytiki/l0_storage/main/AppConfig.java
@@ -80,7 +80,7 @@ public OpenAPI oenAPI(@Value("${springdoc.version}") String appVersion) {
new PathItem().post(
new Operation()
.tags(Collections.singletonList("STORAGE"))
- .operationId("post")
+ .operationId("put")
.summary("Upload a block")
.requestBody(new RequestBody()
.content(new Content()
diff --git a/src/main/java/com/mytiki/l0_storage/security/SecurityConfig.java b/src/main/java/com/mytiki/l0_storage/security/SecurityConfig.java
index 0cbfbbf..9a509cc 100644
--- a/src/main/java/com/mytiki/l0_storage/security/SecurityConfig.java
+++ b/src/main/java/com/mytiki/l0_storage/security/SecurityConfig.java
@@ -6,10 +6,15 @@
package com.mytiki.l0_storage.security;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.mytiki.l0_storage.features.latest.token.TokenController;
import com.mytiki.l0_storage.features.latest.report.ReportController;
+import com.mytiki.l0_storage.features.latest.token.TokenController;
import com.mytiki.l0_storage.utilities.Constants;
import com.mytiki.spring_rest_api.ApiConstants;
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.jwk.source.RemoteJWKSet;
+import com.nimbusds.jose.proc.JWSVerificationKeySelector;
+import com.nimbusds.jose.proc.SecurityContext;
+import com.nimbusds.jwt.proc.DefaultJWTProcessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.Ordered;
@@ -20,13 +25,17 @@
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
+import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
+import org.springframework.security.oauth2.core.OAuth2TokenValidator;
+import org.springframework.security.oauth2.jwt.*;
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
-import java.util.Arrays;
-import java.util.Collections;
+import java.net.URL;
+import java.util.*;
+import java.util.function.Predicate;
@Order(Ordered.HIGHEST_PRECEDENCE)
@EnableWebSecurity
@@ -46,17 +55,30 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final AuthenticationEntryPoint authenticationEntryPoint;
private final String remoteWorkerId;
private final String remoteWorkerSecret;
+ private final URL jwtJwkUri;
+ private final Set jwtJwsAlgorithms;
+ private final Set jwtAudiences;
+ private final String jwtIssuer;
+
private static final String REMOTE_WORKER_ROLE = "REMOTE";
public SecurityConfig(
@Autowired ObjectMapper objectMapper,
@Value("${com.mytiki.l0_storage.remote_worker.id}") String remoteWorkerId,
- @Value("${com.mytiki.l0_storage.remote_worker.secret}") String remoteWorkerSecret) {
+ @Value("${com.mytiki.l0_storage.remote_worker.secret}") String remoteWorkerSecret,
+ @Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}") URL jwtJwkUri,
+ @Value("${spring.security.oauth2.resourceserver.jwt.jws-algorithms}") Set jwtJwsAlgorithms,
+ @Value("${spring.security.oauth2.resourceserver.jwt.audiences}") Set jwtAudiences,
+ @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}") String jwtIssuer) {
super(true);
this.accessDeniedHandler = new AccessDeniedHandler(objectMapper);
this.authenticationEntryPoint = new AuthenticationEntryPoint(objectMapper);
this.remoteWorkerId = remoteWorkerId;
this.remoteWorkerSecret = remoteWorkerSecret;
+ this.jwtJwkUri = jwtJwkUri;
+ this.jwtJwsAlgorithms = jwtJwsAlgorithms;
+ this.jwtAudiences = jwtAudiences;
+ this.jwtIssuer = jwtIssuer;
}
@Override
@@ -91,7 +113,7 @@ protected void configure(HttpSecurity http) throws Exception {
.httpBasic()
.authenticationEntryPoint(authenticationEntryPoint).and()
.oauth2ResourceServer()
- .jwt().and()
+ .jwt().decoder(jwtDecoder()).and()
.accessDeniedHandler(accessDeniedHandler)
.authenticationEntryPoint(authenticationEntryPoint);
}
@@ -104,7 +126,6 @@ protected void configure(AuthenticationManagerBuilder auth) throws Exception {
.roles(REMOTE_WORKER_ROLE);
}
-
private CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOriginPatterns(Collections.singletonList("*"));
@@ -115,4 +136,22 @@ private CorsConfigurationSource corsConfigurationSource() {
source.registerCorsConfiguration("/**", configuration);
return source;
}
+
+ public JwtDecoder jwtDecoder() {
+ DefaultJWTProcessor jwtProcessor = new DefaultJWTProcessor<>();
+ RemoteJWKSet remoteJWKSet = new RemoteJWKSet<>(jwtJwkUri);
+ jwtProcessor.setJWSKeySelector(
+ new JWSVerificationKeySelector<>(jwtJwsAlgorithms, remoteJWKSet));
+ NimbusJwtDecoder decoder = new NimbusJwtDecoder(jwtProcessor);
+ List> validators = new ArrayList<>();
+ validators.add(new JwtTimestampValidator());
+ validators.add(new JwtIssuerValidator(jwtIssuer));
+ validators.add(new JwtClaimValidator<>(JwtClaimNames.SUB, Objects::nonNull));
+ validators.add(new JwtClaimValidator<>(JwtClaimNames.IAT, Objects::nonNull));
+ Predicate> audienceTest = (audience) -> (audience != null)
+ && new HashSet<>(audience).containsAll(jwtAudiences);
+ validators.add(new JwtClaimValidator<>(JwtClaimNames.AUD, audienceTest));
+ decoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(validators));
+ return decoder;
+ }
}
diff --git a/src/test/java/com/mytiki/l0_storage/TokenTest.java b/src/test/java/com/mytiki/l0_storage/TokenTest.java
index 7411662..40c97fe 100644
--- a/src/test/java/com/mytiki/l0_storage/TokenTest.java
+++ b/src/test/java/com/mytiki/l0_storage/TokenTest.java
@@ -24,6 +24,7 @@
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.HttpStatus;
import org.springframework.security.oauth2.jwt.Jwt;
@@ -52,6 +53,7 @@ public class TokenTest {
private ApiIdService apiIdService;
@Autowired
+ @Qualifier("tokenJwtDecoder")
private JwtDecoder jwtDecoder;
@Test
diff --git a/worker/upload/package.json b/worker/upload/package.json
index 993a59f..7902ddc 100644
--- a/worker/upload/package.json
+++ b/worker/upload/package.json
@@ -1,6 +1,6 @@
{
"name": "l0-storage-upload",
- "version": "0.0.12",
+ "version": "0.0.13",
"type": "module",
"devDependencies": {
"@babel/core": "^7.20.2",