From c24330977e3c3117bc92c23bf4f1bde71cdbc49f Mon Sep 17 00:00:00 2001 From: Azher2Ali <121898125+Azher2Ali@users.noreply.github.com> Date: Thu, 1 Jun 2023 13:13:36 -0400 Subject: [PATCH 1/4] Changes related to error status 400 and updates to deletion of visa --- .../ego/controller/VisaController.java | 7 +-- .../model/exceptions/ExceptionHandlers.java | 19 +++++++- .../overture/ego/service/PassportService.java | 45 +++++++------------ .../bio/overture/ego/service/VisaService.java | 44 +++++++++--------- src/main/resources/application.yml | 2 +- 5 files changed, 61 insertions(+), 56 deletions(-) diff --git a/src/main/java/bio/overture/ego/controller/VisaController.java b/src/main/java/bio/overture/ego/controller/VisaController.java index 2ab2453f..a98d6c1b 100644 --- a/src/main/java/bio/overture/ego/controller/VisaController.java +++ b/src/main/java/bio/overture/ego/controller/VisaController.java @@ -125,13 +125,14 @@ public VisaController( * @param visaId UUID */ @AdminScoped - @RequestMapping(method = DELETE, value = "/{id}") + @RequestMapping(method = DELETE, value = "/{type}/{value}") @ResponseStatus(value = HttpStatus.OK) public void deleteVisa( @Parameter(hidden = true) @RequestHeader(value = "Authorization", required = true) final String authorization, - @PathVariable(value = "id", required = true) UUID id) { - visaService.delete(id); + @PathVariable(value = "type", required = true) String type, + @PathVariable(value = "value", required = true) String value) { + visaService.delete(type, value); } /* diff --git a/src/main/java/bio/overture/ego/model/exceptions/ExceptionHandlers.java b/src/main/java/bio/overture/ego/model/exceptions/ExceptionHandlers.java index 0e80a20d..a2871f1a 100644 --- a/src/main/java/bio/overture/ego/model/exceptions/ExceptionHandlers.java +++ b/src/main/java/bio/overture/ego/model/exceptions/ExceptionHandlers.java @@ -1,8 +1,7 @@ package bio.overture.ego.model.exceptions; import static java.lang.String.format; -import static org.springframework.http.HttpStatus.BAD_REQUEST; -import static org.springframework.http.HttpStatus.NOT_FOUND; +import static org.springframework.http.HttpStatus.*; import bio.overture.ego.utils.Joiners; import jakarta.servlet.http.HttpServletRequest; @@ -13,6 +12,7 @@ import lombok.val; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MissingRequestHeaderException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -35,6 +35,21 @@ public ResponseEntity handleConstraintViolationException( NOT_FOUND); } + @ExceptionHandler(MissingRequestHeaderException.class) + public ResponseEntity handleMissingRequestHeaderException( + HttpServletRequest req, MissingRequestHeaderException ex) { + val message = ex.getMessage(); + log.error(message); + return new ResponseEntity( + Map.of( + "message", ex.getMessage(), + "timestamp", new Date(), + "path", req.getServletPath(), + "error", UNAUTHORIZED.getReasonPhrase()), + new HttpHeaders(), + UNAUTHORIZED); + } + @ExceptionHandler(ConstraintViolationException.class) public ResponseEntity handleConstraintViolationException( HttpServletRequest req, ConstraintViolationException ex) { diff --git a/src/main/java/bio/overture/ego/service/PassportService.java b/src/main/java/bio/overture/ego/service/PassportService.java index 8df02148..a6f6f607 100644 --- a/src/main/java/bio/overture/ego/service/PassportService.java +++ b/src/main/java/bio/overture/ego/service/PassportService.java @@ -4,12 +4,9 @@ import bio.overture.ego.model.dto.PassportVisa; import bio.overture.ego.model.entity.Visa; import bio.overture.ego.model.entity.VisaPermission; -import bio.overture.ego.model.exceptions.InvalidTokenException; import bio.overture.ego.utils.CacheUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -43,9 +40,9 @@ public PassportService( public List getPermissions(String authToken) throws JsonProcessingException { // Validates passport auth token - if (!isValidPassport(authToken)) { + /*if (!isValidPassport(authToken)) { throw new InvalidTokenException("The passport token received from broker is invalid"); - } + }*/ // Parses passport JWT token Passport parsedPassport = parsePassport(authToken); // Fetches visas for parsed passport @@ -58,22 +55,12 @@ public List getPermissions(String authToken) throws JsonProcessi } // Validates passport token based on public key - private boolean isValidPassport(@NonNull String authToken) { - Claims claims; - try { - claims = - Jwts.parser() - .setSigningKey(cacheUtil.getPassportBrokerPublicKey()) - .parseClaimsJws(authToken) - .getBody(); - if (claims != null) { - return true; - } - } catch (Exception exception) { - throw new InvalidTokenException("The passport token received from broker is invalid"); - } - return false; - } + /*private void isValidPassport(@NonNull String authToken) throws ParseException, JwkException { + DecodedJWT jwt = JWT.decode(authToken); + Jwk jwk = cacheUtil.getPassportBrokerPublicKey().provider.get(jwt.getKeyId()); + Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null); + algorithm.verify(jwt); + }*/ // Extracts Visas from parsed passport object private List getVisas(Passport passport) { @@ -82,12 +69,12 @@ private List getVisas(Passport passport) { .forEach( visaJwt -> { try { - if (visaService.isValidVisa(visaJwt)) { - PassportVisa visa = visaService.parseVisa(visaJwt); - if (visa != null) { - visas.add(visa); - } + // if (visaService.isValidVisa(visaJwt)) { + PassportVisa visa = visaService.parseVisa(visaJwt); + if (visa != null) { + visas.add(visa); } + // } } catch (JsonProcessingException e) { e.printStackTrace(); } @@ -103,9 +90,11 @@ private List getVisaPermissions(List visas) { .forEach( visa -> { List visaEntities = - visaService.getByTypeAndValue( + visaService.getByTypeAndValueForPassport( visa.getGa4ghVisaV1().getType(), visa.getGa4ghVisaV1().getValue()); - visaPermissions.addAll(visaPermissionService.getPermissionsForVisa(visaEntities)); + if (visaEntities != null && !visaEntities.isEmpty()) { + visaPermissions.addAll(visaPermissionService.getPermissionsForVisa(visaEntities)); + } }); return visaPermissions; } diff --git a/src/main/java/bio/overture/ego/service/VisaService.java b/src/main/java/bio/overture/ego/service/VisaService.java index e4e61f2f..25f0a3c3 100644 --- a/src/main/java/bio/overture/ego/service/VisaService.java +++ b/src/main/java/bio/overture/ego/service/VisaService.java @@ -9,14 +9,11 @@ import bio.overture.ego.model.dto.PassportVisa; import bio.overture.ego.model.dto.VisaRequest; import bio.overture.ego.model.entity.Visa; -import bio.overture.ego.model.exceptions.InvalidTokenException; import bio.overture.ego.model.exceptions.NotFoundException; import bio.overture.ego.repository.VisaRepository; import bio.overture.ego.utils.CacheUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; import jakarta.validation.constraints.NotNull; import java.util.List; import java.util.Optional; @@ -81,9 +78,22 @@ public List getByTypeAndValue(@NonNull String type, @NotNull String value) } } - public void delete(@NonNull UUID id) { - checkExistence(id); - super.delete(id); + public List getByTypeAndValueForPassport(@NonNull String type, @NotNull String value) { + val result = visaRepository.getByTypeAndValue(type, value); + if (!result.isEmpty()) { + return result; + } + return null; + } + + public void delete(@NonNull String type, @NotNull String value) { + List visas = getByTypeAndValue(type, value); + if (visas != null && !visas.isEmpty()) { + visas.stream().forEach(visa -> visaRepository.delete(visa)); + } else { + throw new NotFoundException( + format("No Visa exists with type '%s' and value '%s'", type, value)); + } } // Parses Visa JWT token to convert into Visa Object @@ -99,22 +109,12 @@ public PassportVisa parseVisa(@NonNull String visaJwtToken) throws JsonProcessin } // Checks if the visa is a valid visa - public boolean isValidVisa(@NonNull String authToken) { - Claims claims; - try { - claims = - Jwts.parser() - .setSigningKey(cacheUtil.getPassportBrokerPublicKey()) - .parseClaimsJws(authToken) - .getBody(); - if (claims != null) { - return true; - } - } catch (Exception exception) { - throw new InvalidTokenException("The visa token received from broker is invalid"); - } - return false; - } + /*public void isValidVisa(@NonNull String authToken) throws JwkException { + DecodedJWT jwt = JWT.decode(authToken); + Jwk jwk = cacheUtil.getPassportBrokerPublicKey().provider.get(jwt.getKeyId()); + Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null); + algorithm.verify(jwt); + }*/ public Page listVisa(@NonNull Pageable pageable) { return visaRepository.findAll(pageable); diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index cd5e9f76..544f9417 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -186,7 +186,7 @@ token: broker: publicKey: - url: http://localhost:8082/token/public_key + url: https://login.elixir-czech.org/oidc/jwk # Default values available for creation of entities default: From 2574642f0cfad47c853aa169a0b2d2309ef51cc9 Mon Sep 17 00:00:00 2001 From: Azher2Ali <121898125+Azher2Ali@users.noreply.github.com> Date: Mon, 5 Jun 2023 12:02:22 -0400 Subject: [PATCH 2/4] Changes related to updateVisa Controller and adding JWKS token validation implementation( Commented out for fixing the JAR issue) --- pom.xml | 17 +++- .../ego/controller/VisaController.java | 42 ++++---- .../overture/ego/service/PassportService.java | 5 +- .../bio/overture/ego/service/VisaService.java | 34 ++++--- .../bio/overture/ego/utils/CacheUtil.java | 98 ++++++++++--------- 5 files changed, 104 insertions(+), 92 deletions(-) diff --git a/pom.xml b/pom.xml index b97fcaab..bb8d7473 100644 --- a/pom.xml +++ b/pom.xml @@ -93,7 +93,16 @@ spring-boot-starter-test test - + + + + + + + + + + org.springframework.security spring-security-test @@ -403,12 +412,12 @@ com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier} - - ${basedir}/src/main/proto + com.google.protobuf:protoc:3.21.7:exe:osx-x86_64 + ${basedir}/src/main/proto grpc-java io.grpc:protoc-gen-grpc-java:1.54.0:exe:${os.detected.classifier} - + io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:osx-x86_64 diff --git a/src/main/java/bio/overture/ego/controller/VisaController.java b/src/main/java/bio/overture/ego/controller/VisaController.java index a98d6c1b..6467edde 100644 --- a/src/main/java/bio/overture/ego/controller/VisaController.java +++ b/src/main/java/bio/overture/ego/controller/VisaController.java @@ -1,11 +1,17 @@ package bio.overture.ego.controller; +import static java.lang.String.format; import static org.springframework.web.bind.annotation.RequestMethod.*; -import bio.overture.ego.model.dto.*; -import bio.overture.ego.model.entity.*; +import bio.overture.ego.model.dto.PageDTO; +import bio.overture.ego.model.dto.VisaPermissionRequest; +import bio.overture.ego.model.dto.VisaRequest; +import bio.overture.ego.model.entity.Visa; +import bio.overture.ego.model.entity.VisaPermission; +import bio.overture.ego.model.exceptions.NotFoundException; import bio.overture.ego.security.AdminScoped; -import bio.overture.ego.service.*; +import bio.overture.ego.service.VisaPermissionService; +import bio.overture.ego.service.VisaService; import bio.overture.ego.view.Views; import com.fasterxml.jackson.annotation.JsonView; import io.swagger.v3.oas.annotations.Parameter; @@ -33,22 +39,11 @@ public class VisaController { private final VisaPermissionService visaPermissionService; - private final UserPermissionService userPermissionService; - private final GroupPermissionService groupPermissionService; - private final ApplicationPermissionService applicationPermissionService; - @Autowired public VisaController( - @NonNull VisaService visaService, - @NotNull VisaPermissionService visaPermissionService, - @NonNull UserPermissionService userPermissionService, - @NonNull GroupPermissionService groupPermissionService, - @NonNull ApplicationPermissionService applicationPermissionService) { + @NonNull VisaService visaService, @NotNull VisaPermissionService visaPermissionService) { this.visaService = visaService; this.visaPermissionService = visaPermissionService; - this.groupPermissionService = groupPermissionService; - this.userPermissionService = userPermissionService; - this.applicationPermissionService = applicationPermissionService; } /* @@ -67,7 +62,13 @@ public VisaController( final String authorization, @PathVariable(value = "type", required = true) String type, @PathVariable(value = "value", required = true) String value) { - return visaService.getByTypeAndValue(type, value); + List visas = visaService.getByTypeAndValue(type, value); + if (visas != null && !visas.isEmpty()) { + return visas; + } else { + throw new NotFoundException( + format("No Visa exists with type '%s' and value '%s'", type, value)); + } } /* @@ -110,14 +111,13 @@ public VisaController( * @return Visa visa */ @AdminScoped - @RequestMapping(method = PUT, value = "/{id}") + @RequestMapping(method = PUT, value = "/{type}/{value}") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Update Visa")}) - public @ResponseBody Visa updateVisa( + public @ResponseBody List updateVisa( @Parameter(hidden = true) @RequestHeader(value = "Authorization", required = true) final String authorization, - @PathVariable(value = "id", required = true) UUID id, @RequestBody(required = true) VisaRequest visaRequest) { - return visaService.partialUpdate(id, visaRequest); + return visaService.partialUpdate(visaRequest); } /* @@ -193,7 +193,7 @@ public void deleteVisa( * @param visaPermissionRequest VisaPermissionRequest */ @AdminScoped - @RequestMapping(method = DELETE, value = "/permissions/{policyId}/{visaId}") + @RequestMapping(method = DELETE, value = "/permissions/{policyId}/{type}/{value}") @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "Remove VisaPermission")}) @JsonView(Views.REST.class) public @ResponseBody void removePermissions( diff --git a/src/main/java/bio/overture/ego/service/PassportService.java b/src/main/java/bio/overture/ego/service/PassportService.java index a6f6f607..0e5624ca 100644 --- a/src/main/java/bio/overture/ego/service/PassportService.java +++ b/src/main/java/bio/overture/ego/service/PassportService.java @@ -4,7 +4,6 @@ import bio.overture.ego.model.dto.PassportVisa; import bio.overture.ego.model.entity.Visa; import bio.overture.ego.model.entity.VisaPermission; -import bio.overture.ego.utils.CacheUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.ArrayList; @@ -29,7 +28,7 @@ public class PassportService { @Autowired private VisaPermissionService visaPermissionService; - @Autowired private CacheUtil cacheUtil; + // @Autowired private CacheUtil cacheUtil; @Autowired public PassportService( @@ -90,7 +89,7 @@ private List getVisaPermissions(List visas) { .forEach( visa -> { List visaEntities = - visaService.getByTypeAndValueForPassport( + visaService.getByTypeAndValue( visa.getGa4ghVisaV1().getType(), visa.getGa4ghVisaV1().getValue()); if (visaEntities != null && !visaEntities.isEmpty()) { visaPermissions.addAll(visaPermissionService.getPermissionsForVisa(visaEntities)); diff --git a/src/main/java/bio/overture/ego/service/VisaService.java b/src/main/java/bio/overture/ego/service/VisaService.java index 25f0a3c3..6011106a 100644 --- a/src/main/java/bio/overture/ego/service/VisaService.java +++ b/src/main/java/bio/overture/ego/service/VisaService.java @@ -11,10 +11,10 @@ import bio.overture.ego.model.entity.Visa; import bio.overture.ego.model.exceptions.NotFoundException; import bio.overture.ego.repository.VisaRepository; -import bio.overture.ego.utils.CacheUtil; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.validation.constraints.NotNull; +import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.UUID; @@ -42,7 +42,7 @@ public class VisaService extends AbstractNamedService { /** Dependencies */ @Autowired private VisaRepository visaRepository; - @Autowired private CacheUtil cacheUtil; + // @Autowired private CacheUtil cacheUtil; private final ApiKeyEventsPublisher apiKeyEventsPublisher; @@ -69,16 +69,6 @@ public Visa getById(@NonNull UUID uuid) { } public List getByTypeAndValue(@NonNull String type, @NotNull String value) { - val result = visaRepository.getByTypeAndValue(type, value); - if (!result.isEmpty()) { - return result; - } else { - throw new NotFoundException( - format("No Visa exists with type '%s' and value '%s'", type, value)); - } - } - - public List getByTypeAndValueForPassport(@NonNull String type, @NotNull String value) { val result = visaRepository.getByTypeAndValue(type, value); if (!result.isEmpty()) { return result; @@ -120,10 +110,22 @@ public Page listVisa(@NonNull Pageable pageable) { return visaRepository.findAll(pageable); } - public Visa partialUpdate(@NotNull UUID id, @NonNull VisaRequest updateRequest) { - val visa = getById(id); - VISA_CONVERTER.updateVisa(updateRequest, visa); - return getRepository().save(visa); + public List partialUpdate(@NonNull VisaRequest updateRequest) { + List updatedVisas = new ArrayList<>(); + List visas = getByTypeAndValue(updateRequest.getType(), updateRequest.getValue()); + if (visas != null && !visas.isEmpty()) { + for (Visa visa : visas) { + visa.setBy(updateRequest.getBy()); + visa.setSource(updateRequest.getSource()); + updatedVisas.add(getRepository().save(visa)); + } + } else { + throw new NotFoundException( + format( + "No Visa exists with type '%s' and value '%s'", + updateRequest.getType(), updateRequest.getValue())); + } + return updatedVisas; } @Mapper( diff --git a/src/main/java/bio/overture/ego/utils/CacheUtil.java b/src/main/java/bio/overture/ego/utils/CacheUtil.java index f512a652..c47a1931 100644 --- a/src/main/java/bio/overture/ego/utils/CacheUtil.java +++ b/src/main/java/bio/overture/ego/utils/CacheUtil.java @@ -1,48 +1,50 @@ -package bio.overture.ego.utils; - -import static bio.overture.ego.model.exceptions.InternalServerException.buildInternalServerException; -import static org.springframework.http.HttpMethod.GET; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.http.ResponseEntity; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; -import org.springframework.web.client.HttpStatusCodeException; -import org.springframework.web.client.RestTemplate; - -@Slf4j -@Component -public class CacheUtil { - - @Value("${broker.publicKey.url}") - private String brokerPublicKeyUrl; - - private RestTemplate restTemplate; - - @Cacheable("getPassportBrokerPublicKey") - public String getPassportBrokerPublicKey() { - ResponseEntity response; - try { - response = restTemplate.exchange(brokerPublicKeyUrl, GET, null, String.class); - } catch (HttpStatusCodeException err) { - log.error( - "Invalid {} response from passport broker service: {}", - err.getStatusCode().value(), - err.getMessage()); - throw buildInternalServerException( - "Invalid %s response from passport broker service.", err.getStatusCode().value()); - } - return response.getBody(); - } - - @CacheEvict(value = "evictAll", allEntries = true) - public void evictAllCaches() {} - - @Scheduled(fixedRate = 6000) - public void evictAllCachesEveryDay() { - evictAllCaches(); - } -} +// package bio.overture.ego.utils; +// +// import static +// bio.overture.ego.model.exceptions.InternalServerException.buildInternalServerException; +// import static org.springframework.http.HttpMethod.GET; +// +// import com.nimbusds.jose.jwk.JWKSet; +// import lombok.extern.slf4j.Slf4j; +// import org.springframework.beans.factory.annotation.Value; +// import org.springframework.cache.annotation.CacheEvict; +// import org.springframework.cache.annotation.Cacheable; +// import org.springframework.http.ResponseEntity; +// import org.springframework.scheduling.annotation.Scheduled; +// import org.springframework.stereotype.Component; +// import org.springframework.web.client.HttpStatusCodeException; +// import org.springframework.web.client.RestTemplate; +// +// @Slf4j +// @Component +// public class CacheUtil { +// +// @Value("${broker.publicKey.url}") +// private String brokerPublicKeyUrl; +// +// private RestTemplate restTemplate; +// +// @Cacheable("getPassportBrokerPublicKey") +// public JWKSet getPassportBrokerPublicKey() { +// ResponseEntity response; +// try { +// response = restTemplate.exchange(brokerPublicKeyUrl, GET, null, JWKSet.class); +// } catch (HttpStatusCodeException err) { +// log.error( +// "Invalid {} response from passport broker service: {}", +// err.getStatusCode().value(), +// err.getMessage()); +// throw buildInternalServerException( +// "Invalid %s response from passport broker service.", err.getStatusCode().value()); +// } +// return response.getBody(); +// } +// +// @CacheEvict(value = "evictAll", allEntries = true) +// public void evictAllCaches() {} +// +// @Scheduled(fixedRate = 6000) +// public void evictAllCachesEveryDay() { +// evictAllCaches(); +// } +// } From 18d3acf06229503aa1b20161254c0cc135d8047d Mon Sep 17 00:00:00 2001 From: Azher2Ali <121898125+Azher2Ali@users.noreply.github.com> Date: Tue, 6 Jun 2023 14:05:53 -0400 Subject: [PATCH 3/4] Committing changes related to broker verifier for validating passport and visa --- pom.xml | 20 +-- .../overture/ego/service/PassportService.java | 29 +++-- .../bio/overture/ego/service/VisaService.java | 15 ++- .../bio/overture/ego/utils/CacheUtil.java | 115 ++++++++++-------- 4 files changed, 104 insertions(+), 75 deletions(-) diff --git a/pom.xml b/pom.xml index bb8d7473..a9bb2dc1 100644 --- a/pom.xml +++ b/pom.xml @@ -93,16 +93,16 @@ spring-boot-starter-test test - - - - - - - - - - + + com.auth0 + java-jwt + 4.4.0 + + + com.auth0 + jwks-rsa + 0.22.0 + org.springframework.security spring-security-test diff --git a/src/main/java/bio/overture/ego/service/PassportService.java b/src/main/java/bio/overture/ego/service/PassportService.java index 0e5624ca..a2981d1f 100644 --- a/src/main/java/bio/overture/ego/service/PassportService.java +++ b/src/main/java/bio/overture/ego/service/PassportService.java @@ -4,8 +4,16 @@ import bio.overture.ego.model.dto.PassportVisa; import bio.overture.ego.model.entity.Visa; import bio.overture.ego.model.entity.VisaPermission; +import bio.overture.ego.utils.CacheUtil; +import com.auth0.jwk.Jwk; +import com.auth0.jwk.JwkException; +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.DecodedJWT; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import java.security.interfaces.RSAPublicKey; +import java.text.ParseException; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -28,7 +36,7 @@ public class PassportService { @Autowired private VisaPermissionService visaPermissionService; - // @Autowired private CacheUtil cacheUtil; + @Autowired private CacheUtil cacheUtil; @Autowired public PassportService( @@ -37,11 +45,10 @@ public PassportService( this.visaPermissionService = visaPermissionService; } - public List getPermissions(String authToken) throws JsonProcessingException { + public List getPermissions(String authToken) + throws JsonProcessingException, ParseException, JwkException { // Validates passport auth token - /*if (!isValidPassport(authToken)) { - throw new InvalidTokenException("The passport token received from broker is invalid"); - }*/ + isValidPassport(authToken); // Parses passport JWT token Passport parsedPassport = parsePassport(authToken); // Fetches visas for parsed passport @@ -54,12 +61,13 @@ public List getPermissions(String authToken) throws JsonProcessi } // Validates passport token based on public key - /*private void isValidPassport(@NonNull String authToken) throws ParseException, JwkException { + private void isValidPassport(@NonNull String authToken) + throws ParseException, JwkException, JsonProcessingException { DecodedJWT jwt = JWT.decode(authToken); - Jwk jwk = cacheUtil.getPassportBrokerPublicKey().provider.get(jwt.getKeyId()); + Jwk jwk = cacheUtil.getPassportBrokerPublicKey().get(jwt.getKeyId()); Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null); algorithm.verify(jwt); - }*/ + } // Extracts Visas from parsed passport object private List getVisas(Passport passport) { @@ -68,13 +76,12 @@ private List getVisas(Passport passport) { .forEach( visaJwt -> { try { - // if (visaService.isValidVisa(visaJwt)) { + visaService.isValidVisa(visaJwt); PassportVisa visa = visaService.parseVisa(visaJwt); if (visa != null) { visas.add(visa); } - // } - } catch (JsonProcessingException e) { + } catch (JsonProcessingException | JwkException e) { e.printStackTrace(); } }); diff --git a/src/main/java/bio/overture/ego/service/VisaService.java b/src/main/java/bio/overture/ego/service/VisaService.java index 6011106a..df5aac6c 100644 --- a/src/main/java/bio/overture/ego/service/VisaService.java +++ b/src/main/java/bio/overture/ego/service/VisaService.java @@ -11,9 +11,16 @@ import bio.overture.ego.model.entity.Visa; import bio.overture.ego.model.exceptions.NotFoundException; import bio.overture.ego.repository.VisaRepository; +import bio.overture.ego.utils.CacheUtil; +import com.auth0.jwk.Jwk; +import com.auth0.jwk.JwkException; +import com.auth0.jwt.JWT; +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.DecodedJWT; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.validation.constraints.NotNull; +import java.security.interfaces.RSAPublicKey; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -42,7 +49,7 @@ public class VisaService extends AbstractNamedService { /** Dependencies */ @Autowired private VisaRepository visaRepository; - // @Autowired private CacheUtil cacheUtil; + @Autowired private CacheUtil cacheUtil; private final ApiKeyEventsPublisher apiKeyEventsPublisher; @@ -99,12 +106,12 @@ public PassportVisa parseVisa(@NonNull String visaJwtToken) throws JsonProcessin } // Checks if the visa is a valid visa - /*public void isValidVisa(@NonNull String authToken) throws JwkException { + public void isValidVisa(@NonNull String authToken) throws JwkException, JsonProcessingException { DecodedJWT jwt = JWT.decode(authToken); - Jwk jwk = cacheUtil.getPassportBrokerPublicKey().provider.get(jwt.getKeyId()); + Jwk jwk = cacheUtil.getPassportBrokerPublicKey().get(jwt.getKeyId()); Algorithm algorithm = Algorithm.RSA256((RSAPublicKey) jwk.getPublicKey(), null); algorithm.verify(jwt); - }*/ + } public Page listVisa(@NonNull Pageable pageable) { return visaRepository.findAll(pageable); diff --git a/src/main/java/bio/overture/ego/utils/CacheUtil.java b/src/main/java/bio/overture/ego/utils/CacheUtil.java index c47a1931..3ea88951 100644 --- a/src/main/java/bio/overture/ego/utils/CacheUtil.java +++ b/src/main/java/bio/overture/ego/utils/CacheUtil.java @@ -1,50 +1,65 @@ -// package bio.overture.ego.utils; -// -// import static -// bio.overture.ego.model.exceptions.InternalServerException.buildInternalServerException; -// import static org.springframework.http.HttpMethod.GET; -// -// import com.nimbusds.jose.jwk.JWKSet; -// import lombok.extern.slf4j.Slf4j; -// import org.springframework.beans.factory.annotation.Value; -// import org.springframework.cache.annotation.CacheEvict; -// import org.springframework.cache.annotation.Cacheable; -// import org.springframework.http.ResponseEntity; -// import org.springframework.scheduling.annotation.Scheduled; -// import org.springframework.stereotype.Component; -// import org.springframework.web.client.HttpStatusCodeException; -// import org.springframework.web.client.RestTemplate; -// -// @Slf4j -// @Component -// public class CacheUtil { -// -// @Value("${broker.publicKey.url}") -// private String brokerPublicKeyUrl; -// -// private RestTemplate restTemplate; -// -// @Cacheable("getPassportBrokerPublicKey") -// public JWKSet getPassportBrokerPublicKey() { -// ResponseEntity response; -// try { -// response = restTemplate.exchange(brokerPublicKeyUrl, GET, null, JWKSet.class); -// } catch (HttpStatusCodeException err) { -// log.error( -// "Invalid {} response from passport broker service: {}", -// err.getStatusCode().value(), -// err.getMessage()); -// throw buildInternalServerException( -// "Invalid %s response from passport broker service.", err.getStatusCode().value()); -// } -// return response.getBody(); -// } -// -// @CacheEvict(value = "evictAll", allEntries = true) -// public void evictAllCaches() {} -// -// @Scheduled(fixedRate = 6000) -// public void evictAllCachesEveryDay() { -// evictAllCaches(); -// } -// } +package bio.overture.ego.utils; + +import static bio.overture.ego.model.exceptions.InternalServerException.buildInternalServerException; +import static org.springframework.http.HttpMethod.GET; + +import com.auth0.jwk.Jwk; +import com.fasterxml.jackson.core.JsonProcessingException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.ResponseEntity; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.web.client.HttpStatusCodeException; +import org.springframework.web.client.RestTemplate; + +@Slf4j +@Component +public class CacheUtil { + + @Value("${broker.publicKey.url}") + private String brokerPublicKeyUrl; + + private RestTemplate restTemplate; + + @Cacheable("getPassportBrokerPublicKey") + public Map getPassportBrokerPublicKey() throws JsonProcessingException { + ResponseEntity> response; + Map jwkMap = new HashMap(); + try { + restTemplate = new RestTemplate(); + response = + restTemplate.exchange( + brokerPublicKeyUrl, + GET, + null, + new ParameterizedTypeReference>() {}); + } catch (HttpStatusCodeException err) { + log.error( + "Invalid {} response from passport broker config: {}", + err.getStatusCode().value(), + err.getMessage()); + throw buildInternalServerException( + "Invalid %s response from passport broker config.", err.getStatusCode().value()); + } + for (Object obj : response.getBody().get("keys")) { + Jwk jwkObject = Jwk.fromValues((Map) obj); + jwkMap.put(jwkObject.getId(), jwkObject); + } + return jwkMap; + } + + @CacheEvict(value = "evictAll", allEntries = true) + public void evictAllCaches() {} + + @Scheduled(fixedRate = 6000) + public void evictAllCachesEveryDay() { + evictAllCaches(); + } +} From 4f16469e1f1377655718aa6a6cb65466ee1bfa10 Mon Sep 17 00:00:00 2001 From: Azher2Ali <121898125+Azher2Ali@users.noreply.github.com> Date: Tue, 6 Jun 2023 14:06:38 -0400 Subject: [PATCH 4/4] Committing changes related to broker verifier for validating passport and visa --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index a9bb2dc1..61c4baec 100644 --- a/pom.xml +++ b/pom.xml @@ -412,12 +412,12 @@ com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier} - com.google.protobuf:protoc:3.21.7:exe:osx-x86_64 - ${basedir}/src/main/proto + + ${basedir}/src/main/proto grpc-java io.grpc:protoc-gen-grpc-java:1.54.0:exe:${os.detected.classifier} - io.grpc:protoc-gen-grpc-java:${grpc.version}:exe:osx-x86_64 +