diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti65/Iti65RouteBuilder.java b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti65/Iti65RouteBuilder.java index 52213a6c..ef442b0a 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti65/Iti65RouteBuilder.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti65/Iti65RouteBuilder.java @@ -17,30 +17,14 @@ package ch.bfh.ti.i4mi.mag.mhd.iti65; import static org.openehealth.ipf.platform.camel.ihe.fhir.core.FhirCamelTranslators.translateToFhir; -import static org.openehealth.ipf.platform.camel.ihe.fhir.core.FhirCamelValidators.itiRequestValidator; -import static org.openehealth.ipf.platform.camel.ihe.xds.XdsCamelValidators.iti41RequestValidator; -import java.util.Date; -import java.util.UUID; -import javax.xml.soap.SOAPException; - -import org.apache.camel.Exchange; import org.apache.camel.builder.RouteBuilder; -import org.apache.camel.support.ExpressionAdapter; -import org.hl7.fhir.r4.model.Binary; -import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.DocumentManifest; -import org.hl7.fhir.r4.model.DocumentReference; -import org.hl7.fhir.r4.model.ListResource; -import org.hl7.fhir.r4.model.Resource; import org.openehealth.ipf.commons.ihe.xds.core.ebxml.ebxml30.ProvideAndRegisterDocumentSetRequestType; -import org.openehealth.ipf.commons.ihe.xds.core.responses.QueryResponse; import org.openehealth.ipf.commons.ihe.xds.core.responses.Response; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; import ch.bfh.ti.i4mi.mag.Config; -import ch.bfh.ti.i4mi.mag.mhd.BaseResponseConverter; import ch.bfh.ti.i4mi.mag.mhd.Utils; import ch.bfh.ti.i4mi.mag.xua.AuthTokenConverter; import lombok.extern.slf4j.Slf4j; @@ -81,7 +65,7 @@ public void configure() throws Exception { // pass back errors to the endpoint .errorHandler(noErrorHandler()) //.process(itiRequestValidator()) - .process(AuthTokenConverter.addWsHeader()) + .process(AuthTokenConverter.forwardAuthToken()) // translate, forward, translate back .process(Utils.keepBody()) .process(Utils.storeBodyToHeader("BundleRequest")) diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti66/Iti66RouteBuilder.java b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti66/Iti66RouteBuilder.java index 8477f781..bfc1de1c 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti66/Iti66RouteBuilder.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti66/Iti66RouteBuilder.java @@ -65,7 +65,7 @@ public void configure() throws Exception { from("mhd-iti66-v401:translation?audit=true&auditContext=#myAuditContext").routeId("mdh-documentmanifest-adapter") // pass back errors to the endpoint .errorHandler(noErrorHandler()) - .process(AuthTokenConverter.addWsHeader()).choice() + .process(AuthTokenConverter.forwardAuthToken()).choice() .when(header(Constants.FHIR_REQUEST_PARAMETERS).isNotNull()) .bean(Utils.class,"searchParameterToBody") .bean(Iti66RequestConverter.class).endChoice() diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67RouteBuilder.java b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67RouteBuilder.java index f6ac5e80..8b210333 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67RouteBuilder.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti67/Iti67RouteBuilder.java @@ -88,7 +88,7 @@ public void configure() throws Exception { from("mhd-iti67-v401:translation?audit=true&auditContext=#myAuditContext").routeId("mdh-documentreference-adapter") // pass back errors to the endpoint .errorHandler(noErrorHandler()) - .process(AuthTokenConverter.addWsHeader()) + .process(AuthTokenConverter.forwardAuthToken()) .choice() .when(header(Constants.FHIR_REQUEST_PARAMETERS).isNotNull()) .bean(Utils.class,"searchParameterToBody") diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti68/Iti68RouteBuilder.java b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti68/Iti68RouteBuilder.java index 892cb96a..3ff4b7f8 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti68/Iti68RouteBuilder.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/iti68/Iti68RouteBuilder.java @@ -22,7 +22,6 @@ import org.springframework.stereotype.Component; import ch.bfh.ti.i4mi.mag.Config; -import ch.bfh.ti.i4mi.mag.mhd.Utils; import ch.bfh.ti.i4mi.mag.xua.AuthTokenConverter; import lombok.extern.slf4j.Slf4j; @@ -62,7 +61,7 @@ public void configure() throws Exception { from("mhd-iti68:camel/xdsretrieve?audit=true&auditContext=#myAuditContext").routeId("ddh-retrievedoc-adapter") // pass back errors to the endpoint .errorHandler(noErrorHandler()) - .process(AuthTokenConverter.addWsHeader()) + .process(AuthTokenConverter.forwardAuthToken()) // translate, forward, translate back .bean(Iti68RequestConverter.class) diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/pharm5/Pharm5RouteBuilder.java b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/pharm5/Pharm5RouteBuilder.java index 90df8d6c..c24458fb 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/mhd/pharm5/Pharm5RouteBuilder.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/mhd/pharm5/Pharm5RouteBuilder.java @@ -60,7 +60,7 @@ public void configure() throws Exception { from("mhd-pharm5:translation?audit=true&auditContext=#myAuditContext").routeId("mdh-documentreference-findmedicationlist-adapter") // pass back errors to the endpoint .errorHandler(noErrorHandler()) - .process(AuthTokenConverter.addWsHeader()) + .process(AuthTokenConverter.forwardAuthToken()) .bean(Pharm5RequestConverter.class) .to(endpoint) .process(translateToFhir(new Iti67ResponseConverter(config) , QueryResponse.class)); diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/pmir/iti104/Iti104RouteBuilder.java b/src/main/java/ch/bfh/ti/i4mi/mag/pmir/iti104/Iti104RouteBuilder.java index f066021d..c3b86d67 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/pmir/iti104/Iti104RouteBuilder.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/pmir/iti104/Iti104RouteBuilder.java @@ -26,13 +26,11 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; -import ca.uhn.fhir.rest.api.MethodOutcome; import ch.bfh.ti.i4mi.mag.Config; import ch.bfh.ti.i4mi.mag.mhd.BaseResponseConverter; import ch.bfh.ti.i4mi.mag.mhd.Utils; import ch.bfh.ti.i4mi.mag.pmir.iti78.Iti78RequestConverter; import ch.bfh.ti.i4mi.mag.pmir.iti78.Iti78ResponseConverter; -import ch.bfh.ti.i4mi.mag.pmir.iti83.Iti83ResponseConverter; import ch.bfh.ti.i4mi.mag.xua.AuthTokenConverter; import lombok.extern.slf4j.Slf4j; @@ -88,7 +86,7 @@ public void configure() throws Exception { from("pmir-iti104:stub?audit=true&auditContext=#myAuditContext").routeId("iti104-feed") // pass back errors to the endpoint .errorHandler(noErrorHandler()) - .process(AuthTokenConverter.addWsHeader()) + .process(AuthTokenConverter.forwardAuthToken()) .process(Utils.keepBody()) .bean(Iti104RequestConverter.class) .doTry() diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/pmir/iti78/Iti78RouteBuilder.java b/src/main/java/ch/bfh/ti/i4mi/mag/pmir/iti78/Iti78RouteBuilder.java index 3f38fcd9..e85d3cb9 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/pmir/iti78/Iti78RouteBuilder.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/pmir/iti78/Iti78RouteBuilder.java @@ -66,7 +66,7 @@ public void configure() throws Exception { from("pdqm-iti78:translation?audit=true&auditContext=#myAuditContext").routeId("pdqm-adapter") // pass back errors to the endpoint .errorHandler(noErrorHandler()) - .process(AuthTokenConverter.addWsHeader()) + .process(AuthTokenConverter.forwardAuthToken()) .choice() .when(header(Constants.FHIR_REQUEST_PARAMETERS).isNotNull()) .bean(Iti78RequestConverter.class, "iti78ToIti47Converter") diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/pmir/iti83/Iti83RouteBuilder.java b/src/main/java/ch/bfh/ti/i4mi/mag/pmir/iti83/Iti83RouteBuilder.java index c78aee90..dd9fbc7f 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/pmir/iti83/Iti83RouteBuilder.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/pmir/iti83/Iti83RouteBuilder.java @@ -66,7 +66,7 @@ public void configure() throws Exception { from("pixm-iti83:translation?audit=true&auditContext=#myAuditContext").routeId("pixm-adapter") // pass back errors to the endpoint .errorHandler(noErrorHandler()) - .process(AuthTokenConverter.addWsHeader()) + .process(AuthTokenConverter.forwardAuthToken()) .process(Utils.keepBody()) .bean(Iti83RequestConverter.class) .doTry() diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/pmir/iti93/Iti93RouteBuilder.java b/src/main/java/ch/bfh/ti/i4mi/mag/pmir/iti93/Iti93RouteBuilder.java index cb3ec2c2..18f4eb83 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/pmir/iti93/Iti93RouteBuilder.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/pmir/iti93/Iti93RouteBuilder.java @@ -69,7 +69,7 @@ public void configure() throws Exception { from("pmir-iti93:stub?audit=true&auditContext=#myAuditContext").routeId("pmir-feed") // pass back errors to the endpoint .errorHandler(noErrorHandler()) - .process(AuthTokenConverter.addWsHeader()) + .process(AuthTokenConverter.forwardAuthToken()) .process(Utils.keepBody()) .bean(Iti93RequestConverter.class) .doTry() diff --git a/src/main/java/ch/bfh/ti/i4mi/mag/xua/AuthTokenConverter.java b/src/main/java/ch/bfh/ti/i4mi/mag/xua/AuthTokenConverter.java index c1d03de5..4c47defe 100644 --- a/src/main/java/ch/bfh/ti/i4mi/mag/xua/AuthTokenConverter.java +++ b/src/main/java/ch/bfh/ti/i4mi/mag/xua/AuthTokenConverter.java @@ -16,128 +16,156 @@ package ch.bfh.ti.i4mi.mag.xua; -import static ch.bfh.ti.i4mi.mag.xua.XuaUtils.OASIS_WSSECURITY_NS; -import static org.openehealth.ipf.platform.camel.ihe.ws.AbstractWsEndpoint.OUTGOING_SOAP_HEADERS; -import static org.opensaml.common.xml.SAMLConstants.SAML20_NS; - -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Base64; -import java.util.List; -import java.util.Map; - -import javax.xml.namespace.QName; - import org.apache.camel.Processor; import org.apache.camel.util.CastUtils; import org.apache.cxf.binding.soap.SoapHeader; import org.apache.cxf.headers.Header; import org.apache.cxf.headers.Header.Direction; import org.apache.cxf.staxutils.StaxUtils; +import org.openehealth.ipf.commons.ihe.fhir.Constants; +import org.openehealth.ipf.platform.camel.ihe.ws.AbstractWsEndpoint; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import javax.xml.namespace.QName; +import java.io.StringReader; +import java.util.*; + +import static ch.bfh.ti.i4mi.mag.xua.XuaUtils.OASIS_WSSECURITY_NS; +import static org.openehealth.ipf.platform.camel.ihe.ws.AbstractWsEndpoint.OUTGOING_SOAP_HEADERS; +import static org.opensaml.common.xml.SAMLConstants.SAML20_NS; + /** * Use IHE-SAML Header from request as SOAP wsse Security Header - * @author alexander kreutz * + * @author alexander kreutz */ public class AuthTokenConverter { - - private static final String AUTHORIZATION_HEADER = "Authorization"; - - /** - * This class is not instantiable. - */ - private AuthTokenConverter() { - } - - public static String convert(final String token) { - final String base64Token; - if (token != null && token.startsWith("IHE-SAML ")) { - base64Token = token.substring("IHE-SAML ".length()); - } else if (token != null && token.startsWith("Bearer ")) { - base64Token = token.substring("Bearer ".length()); - } else { - return null; - } - return new String(Base64.getDecoder().decode(base64Token)); - } - - private static String getNodeValue(final Element in, - final String ns, - final String element) { - final NodeList lst = in.getElementsByTagNameNS(ns, element); - if (lst.getLength() == 0) { - return ""; - } - return lst.item(0).getTextContent(); - - } - - private static String getAttrValue(final Element in, - final String ns, - final String element, - final String attribute) { - final NodeList lst = in.getElementsByTagNameNS(ns, element); - if (lst.getLength() == 0) { - return ""; - } - final Node attr = lst.item(0).getAttributes().getNamedItem(attribute); - return attr != null ? attr.getTextContent() : ""; - } - - public static Processor addWsHeader() { - return exchange -> { - - Map> httpHeaders = - CastUtils.cast((Map) exchange.getMessage().getHeader("FhirHttpHeaders")); - - String converted = null; - - if (httpHeaders != null) { - final List header = httpHeaders.get(AUTHORIZATION_HEADER); - if (header != null) { - converted = convert(header.get(0)); - httpHeaders.remove(AUTHORIZATION_HEADER); - } - } else { - final Object authHeader = exchange.getMessage().getHeader(AUTHORIZATION_HEADER); - if (authHeader != null) { - exchange.getMessage().removeHeader(AUTHORIZATION_HEADER); - converted = convert(authHeader.toString()); - } - } - if (converted != null) { - if (converted.startsWith("") + 1); - } - converted = String.format("%s", OASIS_WSSECURITY_NS, converted); - - List soapHeaders = - CastUtils.cast((List) exchange.getIn().getHeader(Header.HEADER_LIST)); - - if (soapHeaders == null) { - soapHeaders = new ArrayList<>(1); - } + private static final Logger log = LoggerFactory.getLogger(AuthTokenConverter.class); + + private static final String AUTHORIZATION_HEADER = "Authorization"; + + /** + * This class is not instantiable. + */ + private AuthTokenConverter() { + } + + private static String getNodeValue(final Element in, + final String ns, + final String element) { + final NodeList lst = in.getElementsByTagNameNS(ns, element); + if (lst.getLength() == 0) { + return ""; + } + return lst.item(0).getTextContent(); + + } + + private static String getAttrValue(final Element in, + final String ns, + final String element, + final String attribute) { + final NodeList lst = in.getElementsByTagNameNS(ns, element); + if (lst.getLength() == 0) { + return ""; + } + final Node attr = lst.item(0).getAttributes().getNamedItem(attribute); + return attr != null ? attr.getTextContent() : ""; + } + + /** + * Forwards the Authorization header to the next hop. + *

+ * If the Authorization header contains a base64-encoded SAML assertion, it is decoded and + * a WS-Security header is created from it. + *

+ *

+ * If the Authorization header is a JWT or something else, it is forwarded as is. + *

+ */ + public static Processor forwardAuthToken() { + return exchange -> { + Map> httpHeaders = + CastUtils.cast((Map) exchange.getMessage().getHeader(Constants.HTTP_INCOMING_HEADERS)); + + // Find the Authorization header in the HTTP headers + String authorizationHeader = null; + if (httpHeaders != null) { + final List header = httpHeaders.get(AUTHORIZATION_HEADER); + if (header != null) { + authorizationHeader = header.get(0); + httpHeaders.remove(AUTHORIZATION_HEADER); + } + } else { + final Object authHeader = exchange.getMessage().getHeader(AUTHORIZATION_HEADER); + if (authHeader != null) { + authorizationHeader = authHeader.toString(); + exchange.getMessage().removeHeader(AUTHORIZATION_HEADER); + } + } + + if (authorizationHeader == null) { + return; + } + + // Extract the payload from the Authorization header + final String payload; + if (authorizationHeader.startsWith("Bearer ")) { + payload = authorizationHeader.substring("Bearer ".length()); + } else if (authorizationHeader.startsWith("IHE-SAML ")) { + payload = authorizationHeader.substring("IHE-SAML ".length()); + } else { + return; + } + + if (payload.startsWith("PHNhbWwyOkFzc2") || payload.startsWith("PD94bW")) { + // It is an encoded SAML assertion, convert it to a WS-Security header + log.debug("Converting encoded SAML assertion to WS-Security header"); + String converted = new String(Base64.getDecoder().decode(payload)); + if (converted.startsWith("") + 1); + } + converted = String.format("%s", + OASIS_WSSECURITY_NS, + converted); + + List soapHeaders = + CastUtils.cast((List) exchange.getIn().getHeader(Header.HEADER_LIST)); + + if (soapHeaders == null) { + soapHeaders = new ArrayList<>(1); + } final Element headerDocument = StaxUtils.read(new StringReader(converted)).getDocumentElement(); - + String alias = getAttrValue(headerDocument, SAML20_NS, "NameID", "SPProvidedID"); String user = getNodeValue(headerDocument, SAML20_NS, "NameID"); String issuer = getNodeValue(headerDocument, SAML20_NS, "Issuer"); - - String userName = alias+"<"+user+"@"+issuer+">"; - - final var newHeader = new SoapHeader(new QName(OASIS_WSSECURITY_NS, "Security"), headerDocument); - newHeader.setDirection(Direction.DIRECTION_OUT); - - soapHeaders.add(newHeader); - - exchange.getMessage().setHeader(OUTGOING_SOAP_HEADERS, soapHeaders); - exchange.setProperty("UserName", userName); - } - }; - } + + String userName = alias + "<" + user + "@" + issuer + ">"; + + final var newHeader = new SoapHeader(new QName(OASIS_WSSECURITY_NS, "Security"), headerDocument); + newHeader.setDirection(Direction.DIRECTION_OUT); + + soapHeaders.add(newHeader); + + exchange.getMessage().setHeader(OUTGOING_SOAP_HEADERS, soapHeaders); + exchange.setProperty("UserName", userName); + } else { + // It is a JWT or something else, just forward it + log.debug("Forwarding Authorization header: {}", authorizationHeader); + final Map outgoingHttpHeaders = + CastUtils.cast(exchange.getMessage().getHeader(AbstractWsEndpoint.OUTGOING_HTTP_HEADERS, + HashMap::new, Map.class)); + + outgoingHttpHeaders.put(AUTHORIZATION_HEADER, authorizationHeader); + + + } + }; + } }