From a249addd5a19040f748b13aba8c4e8608b7dc8ec Mon Sep 17 00:00:00 2001 From: Dominik Frantisek Bucik <bucik@ics.muni.cz> Date: Wed, 3 Apr 2024 20:17:34 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Pretty=20print=20GA4GH?= =?UTF-8?q?=20in=20consent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pretty print GA4GH Visas in consent screen --- .../WEB-INF/tags/common/attributesConsent.tag | 13 +- .../oidc/server/ga4gh/Ga4ghConsentUtils.java | 143 ++++++++++++++++++ 2 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/ga4gh/Ga4ghConsentUtils.java diff --git a/perun-oidc-server-webapp/src/main/webapp/WEB-INF/tags/common/attributesConsent.tag b/perun-oidc-server-webapp/src/main/webapp/WEB-INF/tags/common/attributesConsent.tag index 3f3824d61..1fe2a7738 100644 --- a/perun-oidc-server-webapp/src/main/webapp/WEB-INF/tags/common/attributesConsent.tag +++ b/perun-oidc-server-webapp/src/main/webapp/WEB-INF/tags/common/attributesConsent.tag @@ -1,4 +1,5 @@ -<%@ tag pageEncoding="UTF-8" trimDirectiveWhitespaces="true"%> +<%@ tag pageEncoding="UTF-8" trimDirectiveWhitespaces="true" + import="cz.muni.ics.oidc.server.ga4gh.Ga4ghConsentUtils" %> <%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> <%@ taglib prefix="o" tagdir="/WEB-INF/tags" %> @@ -50,7 +51,15 @@ </c:when> <c:when test="${claim.value.getClass().name eq 'java.util.ArrayList'}"> <c:forEach var="subValue" items="${claim.value}"> - <li>${subValue}</li> + <c:choose> + <c:when test="${claim.key=='ga4gh_passport_v1'}"> + <li><%= Ga4ghConsentUtils.parseAndVerifyVisa( + (String) jspContext.findAttribute("subValue")).getPrettyString() %></li> + </c:when> + <c:otherwise> + <li>${subValue}</li> + </c:otherwise> + </c:choose> </c:forEach> </c:when> <c:otherwise> diff --git a/perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/ga4gh/Ga4ghConsentUtils.java b/perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/ga4gh/Ga4ghConsentUtils.java new file mode 100644 index 000000000..65a060fda --- /dev/null +++ b/perun-oidc-server/src/main/java/cz/muni/ics/oidc/server/ga4gh/Ga4ghConsentUtils.java @@ -0,0 +1,143 @@ +package cz.muni.ics.oidc.server.ga4gh; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nimbusds.jose.Payload; +import com.nimbusds.jwt.JWTParser; +import com.nimbusds.jwt.SignedJWT; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +@Slf4j +public class Ga4ghConsentUtils { + + public static class PassportVisa { + private final String jwt; + private String prettyPayload; + private String sub; + private String iss; + private String type; + private String value; + + public PassportVisa(String jwt) { + this.jwt = jwt; + } + + public String getJwt() { + return jwt; + } + + void setPrettyPayload(String prettyPayload) { + this.prettyPayload = prettyPayload; + } + + public String getPrettyString() { + return prettyPayload; + } + + @Override + public String toString() { + return "PassportVisa{" + + " type=" + type + + ", sub=" + sub + + ", iss=" + iss + + ", value=" + value + + '}'; + } + + public void setSub(String sub) { + this.sub = sub; + } + + public String getSub() { + return sub; + } + + public void setIss(String iss) { + this.iss = iss; + } + + public String getIss() { + return iss; + } + + public void setType(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public void setValue(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } + + public static PassportVisa parseAndVerifyVisa(String jwtString) { + PassportVisa visa = new PassportVisa(jwtString); + try { + SignedJWT signedJWT = (SignedJWT) JWTParser.parse(jwtString); + processPayload(visa, signedJWT.getPayload()); + } catch (Exception ex) { + log.error("visa " + jwtString + " cannot be parsed and verified", ex); + } + return visa; + } + + private static final ObjectMapper JSON_MAPPER = new ObjectMapper(); + + private static void processPayload(PassportVisa visa, Payload payload) throws IOException { + JsonNode doc = JSON_MAPPER.readValue(payload.toString(), JsonNode.class); + checkVisaKey(visa, doc, "sub"); + checkVisaKey(visa, doc, "exp"); + checkVisaKey(visa, doc, "iss"); + JsonNode visaV1 = doc.path("ga4gh_visa_v1"); + checkVisaKey(visa, visaV1, "type"); + checkVisaKey(visa, visaV1, "asserted"); + checkVisaKey(visa, visaV1, "value"); + checkVisaKey(visa, visaV1, "source"); + checkVisaKey(visa, visaV1, "by"); + long exp = doc.get("exp").asLong(); + if (exp < Instant.now().getEpochSecond()) { + return; + } + visa.setPrettyPayload( + visaV1.get("type").asText() + ": \"" + visaV1.get("value").asText() + "\" asserted " + isoDate(visaV1.get("asserted").asLong()) + ); + } + + private static void checkVisaKey(PassportVisa visa, JsonNode jsonNode, String key) { + if (!jsonNode.path(key).isMissingNode()) { + switch (key) { + case "sub": + visa.setSub(jsonNode.path(key).asText()); + break; + case "iss": + visa.setIss(jsonNode.path(key).asText()); + break; + case "type": + visa.setType(jsonNode.path(key).asText()); + break; + case "value": + visa.setValue(jsonNode.path(key).asText()); + break; + default: + } + } + } + + private static String isoDate(long linuxTime) { + return DateTimeFormatter.ISO_LOCAL_DATE.format(ZonedDateTime.ofInstant(Instant.ofEpochSecond(linuxTime), ZoneId.systemDefault())); + } + +} -- GitLab