Skip to content

Commit

Permalink
Enforce jwt usage for did endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
pulledtim committed Aug 21, 2023
1 parent a1aec86 commit 4a2d90d
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 20 deletions.
6 changes: 6 additions & 0 deletions api/did-registry.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/org/fiware/iam/tir/auth/IShareJWT.java
Original file line number Diff line number Diff line change
@@ -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 {
}
35 changes: 25 additions & 10 deletions src/main/java/org/fiware/iam/tir/auth/IShareJwtValidator.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<SignatureConfiguration> signatureConfigurations,
Collection<EncryptionConfiguration> encryptionConfigurations,
Collection<GenericJwtClaimsValidator> 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<Authentication> 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();
}
}
3 changes: 2 additions & 1 deletion src/main/java/org/fiware/iam/tir/rest/DidRegistry.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -48,6 +49,7 @@ public class SatelliteController implements SatelliteApi {
private final CertificateMapper certificateMapper;

@Secured(SecurityRule.IS_AUTHENTICATED)
@IShareJWT
@Override
public HttpResponse<PartiesResponseVO> getParties(@Nullable String eori, @Nullable String certificateSubjectName) {
log.debug("Attempting to retrieve parties with eori {} and certificate subject name", eori, certificateSubjectName);
Expand Down Expand Up @@ -86,6 +88,7 @@ public HttpResponse<PartiesResponseVO> getParties(@Nullable String eori, @Nullab
}

@Secured(SecurityRule.IS_AUTHENTICATED)
@IShareJWT
@Override
public HttpResponse<PartyResponseVO> getPartyById(String partyId) {
log.debug("Attempting to retrieve party {}", partyId);
Expand All @@ -105,6 +108,7 @@ public HttpResponse<PartyResponseVO> getPartyById(String partyId) {
}

@Secured({SecurityRule.IS_ANONYMOUS})
@IShareJWT
@Override
public HttpResponse<TokenResponseVO> getToken(@Nullable String grantType, @Nullable String clientId, @Nullable String scope,
@Nullable String clientAssertionType, @Nullable String clientAssertion) {
Expand Down Expand Up @@ -136,6 +140,7 @@ public HttpResponse<TokenResponseVO> getToken(@Nullable String grantType, @Nulla
}

@Secured(SecurityRule.IS_AUTHENTICATED)
@IShareJWT
@Override
public HttpResponse<TrustedListResponseVO> getTrustedList() {
List<TrustedCAVO> trustedCAs = partiesRepo.getTrustedCAs();
Expand Down
24 changes: 19 additions & 5 deletions src/test/java/org/fiware/iam/tir/rest/DidRegistryIT.java
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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;
Expand All @@ -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);
Expand All @@ -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<DIDDocumentVO> answer = apiClient.getDIDDocument("did:web:someDid",null);
HttpResponse<DIDDocumentVO> answer = apiClient.getDIDDocument(bearerToken, "did:web:someDid", null);
assertEquals(HttpStatus.OK, answer.getStatus());

assertEquals(toJson(SOME_DID_DOCUMENT), toJson(answer.getBody().get()));

}

@SneakyThrows
Expand All @@ -75,14 +81,22 @@ private String toJson(Object obj) {
@Disabled("Test client verifies the parameter already")
@Override
public void getDIDDocument400() throws Exception {
HttpResponse<DIDDocumentVO> answer = apiClient.getDIDDocument(null,null);
HttpResponse<DIDDocumentVO> 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<DIDDocumentVO> answer = apiClient.getDIDDocument("did:ebsi:unknown",null);
HttpResponse<DIDDocumentVO> answer = apiClient.getDIDDocument("did:ebsi:unknown", null);
assertEquals(HttpStatus.NOT_FOUND, answer.getStatus());
}

Expand Down
8 changes: 4 additions & 4 deletions src/test/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ micronaut:
token:
jwt:
enabled: true
# signatures:
# secret:
# generator:
# secret: pleaseChangeThisSecretForANewOne
signatures:
secret:
generator:
secret: pleaseChangeThisSecretForANewOne
http:
services:
read-timeout: 30s
Expand Down

0 comments on commit 4a2d90d

Please sign in to comment.