This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch 1.4.x-fixes in repository https://gitbox.apache.org/repos/asf/cxf-fediz.git
commit d668514a2349c5a5bc01a841228c15a7d4bb02cf Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Fri Jul 13 17:48:03 2018 +0100 FEDIZ-221 - Return errors in a LogoutResponse as well --- .../idp/beans/samlsso/AuthnRequestParser.java | 2 +- .../beans/samlsso/SamlResponseErrorCreator.java | 48 ++++++++++++++++++---- .../webapp/WEB-INF/flows/saml-validate-request.xml | 23 +++++++++-- .../org/apache/cxf/fediz/systests/idp/IdpTest.java | 32 ++++++++++++--- 4 files changed, 86 insertions(+), 19 deletions(-) diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java index 4d5ce10..68f808f 100644 --- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java +++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestParser.java @@ -122,11 +122,11 @@ public class AuthnRequestParser { WebUtils.putAttributeInFlowScope(context, IdpConstants.SAML_AUTHN_REQUEST, authnRequest); } else if (parsedRequest instanceof LogoutRequest) { SAMLLogoutRequest logoutRequest = new SAMLLogoutRequest((LogoutRequest)parsedRequest); + WebUtils.putAttributeInFlowScope(context, IdpConstants.SAML_LOGOUT_REQUEST, logoutRequest); if (logoutRequest.getNotOnOrAfter() != null && (new Date()).after(logoutRequest.getNotOnOrAfter())) { LOG.debug("The LogoutRequest is expired"); throw new ProcessingException(TYPE.BAD_REQUEST); } - WebUtils.putAttributeInFlowScope(context, IdpConstants.SAML_LOGOUT_REQUEST, logoutRequest); } validateRequest(parsedRequest); diff --git a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseErrorCreator.java b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseErrorCreator.java index 761883a..d201d0a 100644 --- a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseErrorCreator.java +++ b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseErrorCreator.java @@ -33,6 +33,7 @@ import org.apache.cxf.helpers.DOMUtils; import org.apache.cxf.rs.security.saml.DeflateEncoderDecoder; import org.apache.wss4j.common.saml.OpenSAMLUtil; import org.apache.wss4j.common.util.DOM2Writer; +import org.opensaml.saml.saml2.core.LogoutResponse; import org.opensaml.saml.saml2.core.Response; import org.opensaml.saml.saml2.core.Status; import org.slf4j.Logger; @@ -48,25 +49,32 @@ public class SamlResponseErrorCreator { private static final Logger LOG = LoggerFactory.getLogger(SamlResponseErrorCreator.class); private boolean supportDeflateEncoding; + private boolean useRealmForIssuer; - public String createSAMLResponse(RequestContext context, boolean requestor, - Idp idp, String requestID) throws ProcessingException { + public String createSAMLResponse(RequestContext context, boolean logout, boolean requestor, + Idp idp, String requestID, String destination) throws ProcessingException { Document doc = DOMUtils.newDocument(); String statusValue = "urn:oasis:names:tc:SAML:2.0:status:Responder"; if (requestor) { statusValue = "urn:oasis:names:tc:SAML:2.0:status:Requester"; } + Status status = SAML2PResponseComponentBuilder.createStatus(statusValue, null); - Response response = - SAML2PResponseComponentBuilder.createSAMLResponse(requestID, idp.getRealm(), status); - + Element responseElement = null; try { - Element policyElement = OpenSAMLUtil.toDom(response, doc); - doc.appendChild(policyElement); + if (logout) { + responseElement = createLogoutResponse(idp, statusValue, destination, requestID); + } else { + Response response = + SAML2PResponseComponentBuilder.createSAMLResponse(requestID, idp.getRealm(), status); + Element policyElement = OpenSAMLUtil.toDom(response, doc); + doc.appendChild(policyElement); + + responseElement = policyElement; + } - Element responseElement = policyElement; return encodeResponse(responseElement); } catch (Exception e) { LOG.warn("Error marshalling SAML Token: {}", e.getMessage()); @@ -74,6 +82,22 @@ public class SamlResponseErrorCreator { } } + protected Element createLogoutResponse(Idp idp, String statusValue, + String destination, String requestID) throws Exception { + Document doc = DOMUtils.newDocument(); + + Status status = + SAML2PResponseComponentBuilder.createStatus(statusValue, null); + String issuer = useRealmForIssuer ? idp.getRealm() : idp.getIdpUrl().toString(); + LogoutResponse response = + SAML2PResponseComponentBuilder.createSAMLLogoutResponse(requestID, issuer, status, destination); + + Element policyElement = OpenSAMLUtil.toDom(response, doc); + doc.appendChild(policyElement); + + return policyElement; + } + protected String encodeResponse(Element response) throws IOException { String responseMessage = DOM2Writer.nodeToString(response); LOG.debug("Created Response: {}", responseMessage); @@ -95,4 +119,12 @@ public class SamlResponseErrorCreator { public void setSupportDeflateEncoding(boolean supportDeflateEncoding) { this.supportDeflateEncoding = supportDeflateEncoding; } + + public boolean isUseRealmForIssuer() { + return useRealmForIssuer; + } + + public void setUseRealmForIssuer(boolean useRealmForIssuer) { + this.useRealmForIssuer = useRealmForIssuer; + } } diff --git a/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml b/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml index 61cdadc..ce6c253 100644 --- a/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml +++ b/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml @@ -272,8 +272,10 @@ result="requestScope.samlAction"/> </on-entry> <!-- See if we managed to at least parse the request to get the response URL --> - <if test="requestScope.samlAction == null or requestScope.samlAction.isEmpty()" - then="viewBadRequestParsingError" else="viewBadRequestResponse"/> + <if test="requestScope.samlAction == null or requestScope.samlAction.isEmpty() or requestScope.samlAction == '/'" + then="viewBadRequestParsingError"/> + <if test="flowScope.saml_logout_request != null" + then="viewBadLogoutRequestResponse" else="viewBadRequestResponse"/> </decision-state> <end-state id="viewBadRequestResponse" view="samlsigninresponseform"> @@ -283,8 +285,21 @@ <evaluate expression="authnRequestParser.retrieveRequestId(flowRequestContext)" result="flowScope.requestId"/> <evaluate expression="flowScope.RelayState" result="requestScope.relayState" /> - <evaluate expression="samlResponseErrorCreator.createSAMLResponse(flowRequestContext, true, flowScope.idpConfig, - flowScope.requestId)" + <evaluate expression="samlResponseErrorCreator.createSAMLResponse(flowRequestContext, false, + true, flowScope.idpConfig, flowScope.requestId, requestScope.samlAction)" + result="requestScope.samlResponse"/> + </on-entry> + </end-state> + + <end-state id="viewBadLogoutRequestResponse" view="samlsignoutresponseform"> + <on-entry> + <evaluate expression="authnRequestParser.retrieveConsumerURL(flowRequestContext)" + result="requestScope.samlAction"/> + <evaluate expression="authnRequestParser.retrieveRequestId(flowRequestContext)" + result="flowScope.requestId"/> + <evaluate expression="flowScope.RelayState" result="requestScope.relayState" /> + <evaluate expression="samlResponseErrorCreator.createSAMLResponse(flowRequestContext, true, + true, flowScope.idpConfig, flowScope.requestId, requestScope.samlAction)" result="requestScope.samlResponse"/> </on-entry> </end-state> diff --git a/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java b/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java index 9ab9f85..068724e 100644 --- a/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java +++ b/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java @@ -1737,6 +1737,10 @@ public class IdpTest { LogoutResponse logoutResponse = (LogoutResponse)OpenSAMLUtil.fromDom(responseDoc.getDocumentElement()); Assert.assertNotNull(logoutResponse); Assert.assertEquals("https://localhost:8080/logout", logoutResponse.getDestination()); + String expectedIssuer = "https://localhost:" + getIdpHttpsPort() + "/fediz-idp/saml"; + Assert.assertEquals(expectedIssuer, logoutResponse.getIssuer().getValue()); + String success = "urn:oasis:names:tc:SAML:2.0:status:Success"; + Assert.assertEquals(success, logoutResponse.getStatus().getStatusCode().getValue()); webClient.close(); @@ -1843,12 +1847,28 @@ public class IdpTest { new UsernamePasswordCredentials(user, password)); webClient.getOptions().setJavaScriptEnabled(false); - try { - webClient.getPage(logoutURL); - Assert.fail("Authentication failure expected"); - } catch (FailingHttpStatusCodeException ex) { - Assert.assertEquals(ex.getStatusCode(), 400); - } + idpPage = webClient.getPage(logoutURL); + webClient.getOptions().setJavaScriptEnabled(true); + + // Check Response + HtmlForm responseForm = idpPage.getFormByName("samlsignoutresponseform"); + Assert.assertEquals("https://localhost:8080/logout", responseForm.getActionAttribute()); + String responseValue = responseForm.getInputByName("SAMLResponse").getAttributeNS(null, "value"); + Assert.assertNotNull(responseValue); + String receivedRelayState = responseForm.getInputByName("RelayState").getAttributeNS(null, "value"); + Assert.assertEquals(relayState, receivedRelayState); + + byte[] deflatedToken = Base64Utility.decode(responseValue); + InputStream tokenStream = new ByteArrayInputStream(deflatedToken); + Document responseDoc = StaxUtils.read(new InputStreamReader(tokenStream, StandardCharsets.UTF_8)); + + LogoutResponse logoutResponse = (LogoutResponse)OpenSAMLUtil.fromDom(responseDoc.getDocumentElement()); + Assert.assertNotNull(logoutResponse); + Assert.assertEquals("https://localhost:8080/logout", logoutResponse.getDestination()); + String expectedIssuer = "https://localhost:" + getIdpHttpsPort() + "/fediz-idp/saml"; + Assert.assertEquals(expectedIssuer, logoutResponse.getIssuer().getValue()); + String success = "urn:oasis:names:tc:SAML:2.0:status:Requester"; + Assert.assertEquals(success, logoutResponse.getStatus().getStatusCode().getValue()); webClient.close(); // 3. now we try to access the idp without authentication but with the existing cookies
