diff --git a/patterns/14efdcb489f3f295fcbdf811_configFile/IDP_AGOV_SEC_ARS_setup.xml b/patterns/14efdcb489f3f295fcbdf811_configFile/IDP_AGOV_SEC_ARS_setup.xml index b094a4e..b3dff09 100644 --- a/patterns/14efdcb489f3f295fcbdf811_configFile/IDP_AGOV_SEC_ARS_setup.xml +++ b/patterns/14efdcb489f3f295fcbdf811_configFile/IDP_AGOV_SEC_ARS_setup.xml @@ -1,5 +1,5 @@ - + diff --git a/patterns/73efd00d67082ff1eb927922_resources/idp_dispatcher.groovy b/patterns/73efd00d67082ff1eb927922_resources/idp_dispatcher.groovy index 2289c32..4156a38 100644 --- a/patterns/73efd00d67082ff1eb927922_resources/idp_dispatcher.groovy +++ b/patterns/73efd00d67082ff1eb927922_resources/idp_dispatcher.groovy @@ -23,13 +23,25 @@ def redirect(String url) { outargs.put('nevis.transfer.destination', url) } -/** - * Extracts the content of the Issuer element from a parsed SAML message. - * The Issuer is optional according to SAML specification but we need it for dispatching. - * - * @param xml - as parsed by Groovy XmlSlurper - * @return text content of Issuer element converted or null - */ +String getNormalisedSamlMessage(String parameter) { + if (parameter == null) { + return + } + String text + byte[] decoded + + // if parameter is raw xml then continue otherwise try to parse the base64 encoding + if (parameter.startsWith("<")) { + text = new String(parameter) + } + else { + decoded = parameter.decodeBase64() + text = new String(decoded) + } + return text +} + + String getNodeText(GPathResult xml, String nodeName) { return xml.depthFirst().find { GPathResult node -> { node.name().endsWith(":${nodeName}") || node.name().equalsIgnoreCase(nodeName) @@ -37,45 +49,46 @@ String getNodeText(GPathResult xml, String nodeName) { }?.text()?.trim() } -String getNodeText(String samlMessage, String nodeName) { +String getAttribute(GPathResult xml, String attributeName) { + return xml.depthFirst().find { GPathResult node -> { + node.attributes().containsKey(attributeName) + } + }?.attributes()?.get(attributeName) +} + +String getNodeText(String parameter, String nodeName) { + String samlMessage = getNormalisedSamlMessage(parameter) if (samlMessage == null) { return } - String text - byte[] decoded def parser = new XmlSlurper() - // if samlMessage is raw xml then continue otherwise try to parse the base64 encoding - if (samlMessage.startsWith("<")) { - text = new String(samlMessage) - } - else { - decoded = samlMessage.decodeBase64() - text = new String(decoded) - } + def xml = parser.parseText(samlMessage) + return getNodeText(xml, nodeName) +} - // after decoded, if redirect binding, we need to parse string to xml - if (text.startsWith("<")) { - // plain String (POST/SOAP parameter) - def xml = parser.parseText(text) - return getNodeText(xml, nodeName) - } - else { - // should be deflate encoded (query parameter) - def is = new InflaterInputStream(new ByteArrayInputStream(decoded), new Inflater(true)) - def xml = parser.parse(is) - return getNodeText(xml, nodeName) +String getAttribute(String parameter, String attributeName) { + String samlMessage = getNormalisedSamlMessage(parameter) + if (samlMessage == null) { + return } + def parser = new XmlSlurper() + def xml = parser.parseText(samlMessage) + return getAttribute(xml, attributeName) } String getIssuer(String value) { return getNodeText(value, 'Issuer') } -String getRequesterID(String value) { - return getNodeText(value, 'RequesterID') +String getAttributeConsumingServiceIndex(String value) { + return getAttribute(value, 'AttributeConsumingServiceIndex') } -def dispatchIssuer(i2s, String issuer, String requester) { +String getProtocolBinding(String value) { + return getAttribute(value, 'ProtocolBinding') +} + +def dispatchIssuer(i2s, String issuer, boolean secureMode) { def result = i2s.get(issuer) if (result == null) { LOG.info("No SP found for issuer '$issuer'. Hint: check SAML SP Connector patterns.") @@ -85,30 +98,33 @@ def dispatchIssuer(i2s, String issuer, String requester) { if(parameters.get('epdMode') == 'artifact' && result == 'epd'){ LOG.debug("EPD: Artifact mode") result = result + "_artifact" - } else if (result == 'main') { - if ('https://op.agov-w.azure.adnovum.net/SAML2/ACS/' == requester) { - result = result + "_secure" - } + } else if (result == 'main' && secureMode) { + LOG.debug("AGOV: Secure mode requested") + result = result + "_secure" } response.setResult(result) - session.put("saml.inbound.issuer", issuer) + session.put('saml.inbound.issuer', issuer) session.put('saml.idp.result', result) // remember decision for sub-sequent requests without a SAML message } def dispatchIssuer(i2s, String issuer) { - dispatchIssuer(i2s, issuer, 'unknown') + dispatchIssuer(i2s, issuer, false) } def dispatchMessage(i2s, String message) { def issuer = getIssuer(message) - def requester = getRequesterID(message) + def secureMode = (getAttributeConsumingServiceIndex(message) == '10101') + def useArtifact = ('urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Artifact' == getProtocolBinding(message)) + + LOG.info("secureMode requested: ${secureMode}") if (issuer == null) { LOG.info("No issuer found in incoming SAML message. Giving up.") } - session.put("saml.inbound.issuer", issuer) - dispatchIssuer(i2s, issuer, requester) + session.put('saml.inbound.issuer', issuer) + session.put('agov.idp.use.artifact', '' + useArtifact) + dispatchIssuer(i2s, issuer, secureMode) } if (parameters.get('logoutConfirmation') == 'true' && "stepup" == request.getMethod()) { diff --git a/patterns/Auth_Realm_Main_IDP_Custom_AGOV_IDP_SEC_bb9e7806a04578e0ad468829.yml b/patterns/Auth_Realm_Main_IDP_Custom_AGOV_IDP_SEC_bb9e7806a04578e0ad468829.yml index c7f42bd..8d9a044 100644 --- a/patterns/Auth_Realm_Main_IDP_Custom_AGOV_IDP_SEC_bb9e7806a04578e0ad468829.yml +++ b/patterns/Auth_Realm_Main_IDP_Custom_AGOV_IDP_SEC_bb9e7806a04578e0ad468829.yml @@ -7,9 +7,7 @@ pattern: notes: "modified script taken from what Nevis generated when using a SAM IDP Pattern" properties: authStatesFile: "res://bb9e7806a04578e0ad468829#authStatesFile" - parameters: "out.binding: http-post\nout.post.relayStateEncoding: HTML\nout.encrypt:\ - \ Assertion\nout.encrypt.keystoreref: EncryptionKeys\nout.encryption_key_from_expression:\ - \ \nout.encrypt.keyobjectref: DefaultEncryptionKey\n" + parameters: "var://idp_sp_sec_settings" onSuccess: - "pattern://2f81f8b878ef787fc5cc284a" onFailure: diff --git a/patterns/bb9e7806a04578e0ad468829_authStatesFile/agov_idp_sec.xml b/patterns/bb9e7806a04578e0ad468829_authStatesFile/agov_idp_sec.xml index b67507c..94ba261 100644 --- a/patterns/bb9e7806a04578e0ad468829_authStatesFile/agov_idp_sec.xml +++ b/patterns/bb9e7806a04578e0ad468829_authStatesFile/agov_idp_sec.xml @@ -1,45 +1,76 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/variables.yml b/variables.yml index edc5f77..1f6c384 100644 --- a/variables.yml +++ b/variables.yml @@ -550,6 +550,17 @@ variables: format: "^[^\\s,]*$" value: "atb-sec" requireOverloading: true + idp_sp_sec_settings: + className: "ch.nevis.admin.v4.plugin.base.generation.property.TextProperty" + parameters: + required: false + syntax: "YAML" + value: | + out.post.relayStateEncoding: HTML + out.encrypt: Assertion + out.encrypt.keystoreref: EncryptionKeys + out.encrypt.keyobjectref: DefaultEncryptionKey + requireOverloading: false internal-idp-auth-signer-trust-additional-trusted-certificates: className: "ch.nevis.admin.v4.plugin.base.generation.property.AttachmentProperty" parameters: