diff --git a/api/did-registry.yaml b/api/did-registry.yaml index 50dfc52..b482cae 100644 --- a/api/did-registry.yaml +++ b/api/did-registry.yaml @@ -31,6 +31,12 @@ paths: application/json: schema: $ref: '#/components/schemas/ProblemDetails' + '401': + description: Unauthorized + content: + application/json: + schema: + $ref: '#/components/schemas/ProblemDetails' '404': description: Not found content: diff --git a/src/main/java/org/fiware/iam/tir/auth/IShareJWT.java b/src/main/java/org/fiware/iam/tir/auth/IShareJWT.java new file mode 100644 index 0000000..b3fdf65 --- /dev/null +++ b/src/main/java/org/fiware/iam/tir/auth/IShareJWT.java @@ -0,0 +1,13 @@ +package org.fiware.iam.tir.auth; + +import java.lang.annotation.*; + +/** + * Annotation to force requiring a iShare formatted JWT token + */ +@Target({ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface IShareJWT { +} \ No newline at end of file diff --git a/src/main/java/org/fiware/iam/tir/auth/IShareJwtValidator.java b/src/main/java/org/fiware/iam/tir/auth/IShareJwtValidator.java index 66e5393..ae7c7e6 100644 --- a/src/main/java/org/fiware/iam/tir/auth/IShareJwtValidator.java +++ b/src/main/java/org/fiware/iam/tir/auth/IShareJwtValidator.java @@ -10,6 +10,7 @@ import io.micronaut.security.token.jwt.validator.JwtAuthenticationFactory; import io.micronaut.security.token.jwt.validator.JwtTokenValidator; import io.micronaut.security.token.jwt.validator.JwtValidator; +import io.micronaut.web.router.MethodBasedRouteMatch; import jakarta.inject.Singleton; import org.fiware.iam.tir.repository.PartiesRepo; import org.reactivestreams.Publisher; @@ -21,35 +22,49 @@ @Replaces(JwtTokenValidator.class) public class IShareJwtValidator extends JwtTokenValidator { + /** + * Attribute used by Micronaut to hold the route mapping info + */ + private static final String MICRONAUT_HTTP_ROUTE_INFO_ATTRIBUTE = "micronaut.http.route.info"; private final JWTService jwtService; - private final PartiesRepo partiesRepo; public IShareJwtValidator( Collection signatureConfigurations, Collection encryptionConfigurations, Collection genericJwtClaimsValidators, JwtAuthenticationFactory jwtAuthenticationFactory, - JWTService jwtService, PartiesRepo partiesRepo) { + JWTService jwtService) { super(signatureConfigurations, encryptionConfigurations, genericJwtClaimsValidators, jwtAuthenticationFactory); this.jwtService = jwtService; - this.partiesRepo = partiesRepo; } public IShareJwtValidator(JwtValidator validator, JwtAuthenticationFactory jwtAuthenticationFactory, - JWTService jwtService, PartiesRepo partiesRepo) { + JWTService jwtService) { super(validator, jwtAuthenticationFactory); this.jwtService = jwtService; - this.partiesRepo = partiesRepo; } @Override public Publisher validateToken(String token, HttpRequest request) { - try { - DecodedJWT decodedJWT = jwtService.validateJWT(token); - return Flux.just(Authentication.build(decodedJWT.getClaim("client_id").asString())); - } catch (Exception e) { - return Flux.empty(); + if(isIShareTokenRequired(request)) { + try { + DecodedJWT decodedJWT = jwtService.validateJWT(token); + return Flux.just(Authentication.build(decodedJWT.getClaim("client_id").asString())); + } catch (Exception e) { + return Flux.empty(); + } + }else{ + return super.validateToken(token,request); } } + + private boolean isIShareTokenRequired(HttpRequest request){ + return request + .getAttribute(MICRONAUT_HTTP_ROUTE_INFO_ATTRIBUTE) + .filter(MethodBasedRouteMatch.class::isInstance) + .map(MethodBasedRouteMatch.class::cast) + .filter(e -> e.hasAnnotation(IShareJWT.class)) + .isPresent(); + } } diff --git a/src/main/java/org/fiware/iam/tir/rest/DidRegistry.java b/src/main/java/org/fiware/iam/tir/rest/DidRegistry.java index 1422a65..f67e283 100644 --- a/src/main/java/org/fiware/iam/tir/rest/DidRegistry.java +++ b/src/main/java/org/fiware/iam/tir/rest/DidRegistry.java @@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j; import org.fiware.iam.did.api.DidApi; import org.fiware.iam.did.model.DIDDocumentVO; +import org.fiware.iam.tir.auth.IShareJWT; import org.fiware.iam.tir.configuration.Party; import org.fiware.iam.tir.repository.DidDocumentMapper; import org.fiware.iam.tir.repository.PartiesRepo; @@ -23,7 +24,7 @@ @RequiredArgsConstructor @Slf4j @Controller("${general.basepath:/}") -@Secured(SecurityRule.IS_ANONYMOUS) +@Secured(SecurityRule.IS_AUTHENTICATED) public class DidRegistry implements DidApi { private final DidDocumentMapper didDocumentMapper; diff --git a/src/main/java/org/fiware/iam/tir/rest/SatelliteController.java b/src/main/java/org/fiware/iam/tir/rest/SatelliteController.java index 3f305a4..cb65509 100644 --- a/src/main/java/org/fiware/iam/tir/rest/SatelliteController.java +++ b/src/main/java/org/fiware/iam/tir/rest/SatelliteController.java @@ -15,6 +15,7 @@ import org.fiware.iam.satellite.api.SatelliteApi; import org.fiware.iam.satellite.model.*; import org.fiware.iam.tir.auth.CertificateMapper; +import org.fiware.iam.tir.auth.IShareJWT; import org.fiware.iam.tir.auth.JWTService; import org.fiware.iam.tir.configuration.Party; import org.fiware.iam.tir.configuration.SatelliteProperties; @@ -48,6 +49,7 @@ public class SatelliteController implements SatelliteApi { private final CertificateMapper certificateMapper; @Secured(SecurityRule.IS_AUTHENTICATED) + @IShareJWT @Override public HttpResponse getParties(@Nullable String eori, @Nullable String certificateSubjectName) { log.debug("Attempting to retrieve parties with eori {} and certificate subject name", eori, certificateSubjectName); @@ -86,6 +88,7 @@ public HttpResponse getParties(@Nullable String eori, @Nullab } @Secured(SecurityRule.IS_AUTHENTICATED) + @IShareJWT @Override public HttpResponse getPartyById(String partyId) { log.debug("Attempting to retrieve party {}", partyId); @@ -105,6 +108,7 @@ public HttpResponse getPartyById(String partyId) { } @Secured({SecurityRule.IS_ANONYMOUS}) + @IShareJWT @Override public HttpResponse getToken(@Nullable String grantType, @Nullable String clientId, @Nullable String scope, @Nullable String clientAssertionType, @Nullable String clientAssertion) { @@ -136,6 +140,7 @@ public HttpResponse getToken(@Nullable String grantType, @Nulla } @Secured(SecurityRule.IS_AUTHENTICATED) + @IShareJWT @Override public HttpResponse getTrustedList() { List trustedCAs = partiesRepo.getTrustedCAs(); diff --git a/src/test/java/org/fiware/iam/tir/rest/DidRegistryIT.java b/src/test/java/org/fiware/iam/tir/rest/DidRegistryIT.java index bc93e06..0ecf8cf 100644 --- a/src/test/java/org/fiware/iam/tir/rest/DidRegistryIT.java +++ b/src/test/java/org/fiware/iam/tir/rest/DidRegistryIT.java @@ -1,9 +1,11 @@ package org.fiware.iam.tir.rest; +import changeMe.JwtProvider; import com.fasterxml.jackson.databind.ObjectMapper; import io.github.wistefan.mapping.JavaObjectMapper; import io.micronaut.http.HttpResponse; import io.micronaut.http.HttpStatus; +import io.micronaut.security.token.jwt.signature.SignatureGeneratorConfiguration; import io.micronaut.test.annotation.MockBean; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; import lombok.SneakyThrows; @@ -22,6 +24,7 @@ import java.util.Optional; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -36,6 +39,8 @@ public class DidRegistryIT extends NGSIBasedTest implements DidApiTestSpec { public final DidService didService; + private final SignatureGeneratorConfiguration signature; + @MockBean(DidService.class) public DidService mockDidService() { return mock(DidService.class); @@ -45,26 +50,27 @@ public DidService mockDidService() { .id("did:web:someDid") .addVerificationMethodItem(new JsonWebKey2020VerificationMethodVO().id("did:web:someDid").publicKeyJwk(new JWKVO().x5u("example.com/cert"))); - public DidRegistryIT(EntitiesApiClient entitiesApiClient, JavaObjectMapper javaObjectMapper, ObjectMapper objectMapper, GeneralProperties generalProperties, DidApiTestClient apiClient, InMemoryPartiesRepo partyRepo, DidService didService) { + public DidRegistryIT(EntitiesApiClient entitiesApiClient, JavaObjectMapper javaObjectMapper, ObjectMapper objectMapper, GeneralProperties generalProperties, DidApiTestClient apiClient, InMemoryPartiesRepo partyRepo, DidService didService, SignatureGeneratorConfiguration signature) { super(entitiesApiClient, javaObjectMapper, objectMapper, generalProperties); this.apiClient = apiClient; this.partyRepo = partyRepo; this.didService = didService; + this.signature = signature; } @Test @Override public void getDIDDocument200() throws Exception { + String bearerToken = new JwtProvider(signature).builder().toBearer(); when(didService.retrieveDidDocument("did:web:someDid")).thenReturn(Optional.of(SOME_DID_DOCUMENT)); when(didService.getCertificate(SOME_DID_DOCUMENT)).thenReturn(Optional.of("someCert")); createIssuer(new TrustedIssuer("did:web:someId").setIssuer("did:web:someDid")); partyRepo.updateParties(); - HttpResponse answer = apiClient.getDIDDocument("did:web:someDid",null); + HttpResponse answer = apiClient.getDIDDocument(bearerToken, "did:web:someDid", null); assertEquals(HttpStatus.OK, answer.getStatus()); assertEquals(toJson(SOME_DID_DOCUMENT), toJson(answer.getBody().get())); - } @SneakyThrows @@ -75,14 +81,22 @@ private String toJson(Object obj) { @Disabled("Test client verifies the parameter already") @Override public void getDIDDocument400() throws Exception { - HttpResponse answer = apiClient.getDIDDocument(null,null); + HttpResponse answer = apiClient.getDIDDocument(null, null); assertEquals(HttpStatus.BAD_REQUEST, answer.getStatus()); } + @Test + @Override + public void getDIDDocument401() throws Exception { + assertThat(callAndCatch(() -> apiClient.getDIDDocument(null, "did:ebsi:unknown", null))) + .extracting(HttpResponse::getStatus) + .isEqualTo(HttpStatus.UNAUTHORIZED); + } + @Test @Override public void getDIDDocument404() throws Exception { - HttpResponse answer = apiClient.getDIDDocument("did:ebsi:unknown",null); + HttpResponse answer = apiClient.getDIDDocument("did:ebsi:unknown", null); assertEquals(HttpStatus.NOT_FOUND, answer.getStatus()); } diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml index 7831cf1..b1fd936 100644 --- a/src/test/resources/application.yaml +++ b/src/test/resources/application.yaml @@ -14,10 +14,10 @@ micronaut: token: jwt: enabled: true -# signatures: -# secret: -# generator: -# secret: pleaseChangeThisSecretForANewOne + signatures: + secret: + generator: + secret: pleaseChangeThisSecretForANewOne http: services: read-timeout: 30s