Return-Path: X-Original-To: apmail-cxf-commits-archive@www.apache.org Delivered-To: apmail-cxf-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 404EC19AF4 for ; Wed, 30 Mar 2016 16:43:52 +0000 (UTC) Received: (qmail 93494 invoked by uid 500); 30 Mar 2016 16:43:52 -0000 Delivered-To: apmail-cxf-commits-archive@cxf.apache.org Received: (qmail 93436 invoked by uid 500); 30 Mar 2016 16:43:52 -0000 Mailing-List: contact commits-help@cxf.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@cxf.apache.org Delivered-To: mailing list commits@cxf.apache.org Received: (qmail 93427 invoked by uid 99); 30 Mar 2016 16:43:52 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Wed, 30 Mar 2016 16:43:52 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id BB16FDFD5B; Wed, 30 Mar 2016 16:43:51 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: coheigea@apache.org To: commits@cxf.apache.org Date: Wed, 30 Mar 2016 16:43:51 -0000 Message-Id: <6b6f65f1e7fa457fb67bc47ba63a3db6@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [1/2] cxf-fediz git commit: Create a SAML Response on an error Repository: cxf-fediz Updated Branches: refs/heads/master a1fba8d71 -> 9da48791d Create a SAML Response on an error Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/9da48791 Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/9da48791 Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/9da48791 Branch: refs/heads/master Commit: 9da48791d6d31366052518c9c6ccd25bdf82a4ca Parents: c564a8b Author: Colm O hEigeartaigh Authored: Wed Mar 30 17:14:51 2016 +0100 Committer: Colm O hEigeartaigh Committed: Wed Mar 30 17:43:11 2016 +0100 ---------------------------------------------------------------------- .../beans/samlsso/AuthnRequestValidator.java | 2 +- .../idp/beans/samlsso/SamlResponseCreator.java | 1 - .../beans/samlsso/SamlResponseErrorCreator.java | 97 ++++++++++ .../WEB-INF/flows/saml-signin-request.xml | 13 +- .../WEB-INF/flows/saml-validate-request.xml | 19 +- .../apache/cxf/fediz/systests/idp/IdpTest.java | 183 +++++++++---------- 6 files changed, 202 insertions(+), 113 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/9da48791/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestValidator.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestValidator.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestValidator.java index cb90ed0..80f4d0c 100644 --- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestValidator.java +++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/AuthnRequestValidator.java @@ -152,7 +152,7 @@ public class AuthnRequestValidator { LOG.debug("Validating destination: {}", destination); String localAddr = WebUtils.getHttpServletRequest(context).getRequestURL().toString(); - if (!localAddr.startsWith(destination)) { + if (destination == null || !localAddr.startsWith(destination)) { LOG.debug("The destination {} does not match the local address {}", destination, localAddr); throw new ProcessingException(TYPE.BAD_REQUEST); } http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/9da48791/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java index c0c0d22..a9aadf5 100644 --- a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java +++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseCreator.java @@ -89,7 +89,6 @@ public class SamlResponseCreator { Element response = createResponse(idp, requestId, saml2Assertion); return encodeResponse(response); } catch (Exception ex) { - ex.printStackTrace(); LOG.warn("Error marshalling SAML Token: {}", ex.getMessage()); throw new ProcessingException(TYPE.BAD_REQUEST); } http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/9da48791/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseErrorCreator.java ---------------------------------------------------------------------- diff --git a/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseErrorCreator.java b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseErrorCreator.java new file mode 100644 index 0000000..24b21f4 --- /dev/null +++ b/services/idp/src/main/java/org/apache/cxf/fediz/service/idp/beans/samlsso/SamlResponseErrorCreator.java @@ -0,0 +1,97 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.fediz.service.idp.beans.samlsso; + +import java.io.IOException; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.apache.cxf.common.util.Base64Utility; +import org.apache.cxf.fediz.core.exception.ProcessingException; +import org.apache.cxf.fediz.core.exception.ProcessingException.TYPE; +import org.apache.cxf.fediz.service.idp.domain.Idp; +import org.apache.cxf.fediz.service.idp.samlsso.SAML2PResponseComponentBuilder; +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.Response; +import org.opensaml.saml.saml2.core.Status; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.webflow.execution.RequestContext; + +/** + * Create a SAML Error Response + */ +@Component +public class SamlResponseErrorCreator { + + private static final Logger LOG = LoggerFactory.getLogger(SamlResponseErrorCreator.class); + private boolean supportDeflateEncoding = true; + + public String createSAMLResponse(RequestContext context, boolean requestor, + Idp idp, String requestID) 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); + + try { + Element policyElement = OpenSAMLUtil.toDom(response, doc); + doc.appendChild(policyElement); + + Element responseElement = policyElement; + return encodeResponse(responseElement); + } catch (Exception e) { + LOG.warn("Error marshalling SAML Token: {}", e.getMessage()); + throw new ProcessingException(TYPE.BAD_REQUEST); + } + } + + protected String encodeResponse(Element response) throws IOException { + String responseMessage = DOM2Writer.nodeToString(response); + LOG.debug("Created Response: {}", responseMessage); + + if (supportDeflateEncoding) { + DeflateEncoderDecoder encoder = new DeflateEncoderDecoder(); + byte[] deflatedBytes = encoder.deflateToken(responseMessage.getBytes("UTF-8")); + + return Base64Utility.encode(deflatedBytes); + } + + return Base64Utility.encode(responseMessage.getBytes()); + } + + public boolean isSupportDeflateEncoding() { + return supportDeflateEncoding; + } + + public void setSupportDeflateEncoding(boolean supportDeflateEncoding) { + this.supportDeflateEncoding = supportDeflateEncoding; + } +} http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/9da48791/services/idp/src/main/webapp/WEB-INF/flows/saml-signin-request.xml ---------------------------------------------------------------------- diff --git a/services/idp/src/main/webapp/WEB-INF/flows/saml-signin-request.xml b/services/idp/src/main/webapp/WEB-INF/flows/saml-signin-request.xml index a609ae1..70f7452 100644 --- a/services/idp/src/main/webapp/WEB-INF/flows/saml-signin-request.xml +++ b/services/idp/src/main/webapp/WEB-INF/flows/saml-signin-request.xml @@ -34,7 +34,7 @@ + then="scInternalServerError" /> @@ -52,7 +52,7 @@ - + @@ -103,7 +103,12 @@ - - + + + + + + + http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/9da48791/services/idp/src/main/webapp/WEB-INF/flows/saml-validate-request.xml ---------------------------------------------------------------------- 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 841a5d5..ef6d813 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 @@ -52,7 +52,9 @@ - + + + @@ -96,12 +98,17 @@ - - + + - - + + + + http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/9da48791/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/idp/IdpTest.java ---------------------------------------------------------------------- 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 aa974be..2af63be 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 @@ -32,7 +32,6 @@ import java.util.UUID; import org.w3c.dom.Document; import org.w3c.dom.Element; -import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; import com.gargoylesoftware.htmlunit.WebClient; import com.gargoylesoftware.htmlunit.html.DomElement; import com.gargoylesoftware.htmlunit.html.DomNodeList; @@ -204,46 +203,13 @@ public class IdpTest { webClient.getOptions().setJavaScriptEnabled(true); Assert.assertEquals("IDP SignIn Response Form", idpPage.getTitleText()); - // Parse the form to get the token (SAMLResponse) - DomNodeList results = idpPage.getElementsByTagName("input"); - - String samlResponse = null; - boolean foundRelayState = false; - for (DomElement result : results) { - if ("SAMLResponse".equals(result.getAttributeNS(null, "name"))) { - samlResponse = result.getAttributeNS(null, "value"); - } else if ("RelayState".equals(result.getAttributeNS(null, "name"))) { - foundRelayState = true; - Assert.assertEquals(result.getAttributeNS(null, "value"), relayState); - } - } - - Assert.assertNotNull(samlResponse); - Assert.assertTrue(foundRelayState); - - // Check the "action" - DomNodeList formResults = idpPage.getElementsByTagName("form"); - Assert.assertFalse(formResults.isEmpty()); - - DomElement formResult = formResults.get(0); - String action = formResult.getAttributeNS(null, "action"); - Assert.assertTrue(action.equals(consumerURL)); - - // Decode + verify response - byte[] deflatedToken = Base64Utility.decode(samlResponse); - InputStream inputStream = new DeflateEncoderDecoder().inflateToken(deflatedToken); - - Document responseDoc = StaxUtils.read(new InputStreamReader(inputStream, "UTF-8")); - - XMLObject responseObject = OpenSAMLUtil.fromDom(responseDoc.getDocumentElement()); - Assert.assertTrue(responseObject instanceof org.opensaml.saml.saml2.core.Response); - - org.opensaml.saml.saml2.core.Response samlResponseObject = - (org.opensaml.saml.saml2.core.Response)responseObject; - Assert.assertTrue(authnRequest.getID().equals(samlResponseObject.getInResponseTo())); + org.opensaml.saml.saml2.core.Response samlResponse = + parseSAMLResponse(idpPage, relayState, consumerURL, authnRequest.getID()); + String expected = "urn:oasis:names:tc:SAML:2.0:status:Success"; + Assert.assertEquals(expected, samlResponse.getStatus().getStatusCode().getValue()); // Check claims - String parsedResponse = DOM2Writer.nodeToString(responseDoc); + String parsedResponse = DOM2Writer.nodeToString(samlResponse.getDOM().getOwnerDocument()); String claim = ClaimTypes.FIRSTNAME.toString(); Assert.assertTrue(parsedResponse.contains(claim)); claim = ClaimTypes.LASTNAME.toString(); @@ -291,13 +257,13 @@ public class IdpTest { new UsernamePasswordCredentials(user, password)); webClient.getOptions().setJavaScriptEnabled(false); - try { - webClient.getPage(url); - Assert.fail("Failure expected on a bad issuer value"); - } catch (FailingHttpStatusCodeException ex) { - Assert.assertEquals(ex.getStatusCode(), 400); - } + final HtmlPage idpPage = webClient.getPage(url); + org.opensaml.saml.saml2.core.Response samlResponse = + parseSAMLResponse(idpPage, relayState, consumerURL, authnRequest.getID()); + String expected = "urn:oasis:names:tc:SAML:2.0:status:Requester"; + Assert.assertEquals(expected, samlResponse.getStatus().getStatusCode().getValue()); + webClient.close(); } @@ -337,12 +303,12 @@ public class IdpTest { new UsernamePasswordCredentials(user, password)); webClient.getOptions().setJavaScriptEnabled(false); - try { - webClient.getPage(url); - Assert.fail("Failure expected on no destination value"); - } catch (FailingHttpStatusCodeException ex) { - // expected - } + final HtmlPage idpPage = webClient.getPage(url); + + org.opensaml.saml.saml2.core.Response samlResponse = + parseSAMLResponse(idpPage, relayState, consumerURL, authnRequest.getID()); + String expected = "urn:oasis:names:tc:SAML:2.0:status:Requester"; + Assert.assertEquals(expected, samlResponse.getStatus().getStatusCode().getValue()); webClient.close(); } @@ -383,12 +349,12 @@ public class IdpTest { new UsernamePasswordCredentials(user, password)); webClient.getOptions().setJavaScriptEnabled(false); - try { - webClient.getPage(url); - Assert.fail("Failure expected on an unsigned request"); - } catch (FailingHttpStatusCodeException ex) { - Assert.assertEquals(ex.getStatusCode(), 400); - } + final HtmlPage idpPage = webClient.getPage(url); + + org.opensaml.saml.saml2.core.Response samlResponse = + parseSAMLResponse(idpPage, relayState, consumerURL, authnRequest.getID()); + String expected = "urn:oasis:names:tc:SAML:2.0:status:Requester"; + Assert.assertEquals(expected, samlResponse.getStatus().getStatusCode().getValue()); webClient.close(); } @@ -457,46 +423,13 @@ public class IdpTest { webClient.getOptions().setJavaScriptEnabled(true); Assert.assertEquals("IDP SignIn Response Form", idpPage.getTitleText()); - // Parse the form to get the token (SAMLResponse) - DomNodeList results = idpPage.getElementsByTagName("input"); - - String samlResponse = null; - boolean foundRelayState = false; - for (DomElement result : results) { - if ("SAMLResponse".equals(result.getAttributeNS(null, "name"))) { - samlResponse = result.getAttributeNS(null, "value"); - } else if ("RelayState".equals(result.getAttributeNS(null, "name"))) { - foundRelayState = true; - Assert.assertEquals(result.getAttributeNS(null, "value"), relayState); - } - } - - Assert.assertNotNull(samlResponse); - Assert.assertTrue(foundRelayState); - - // Check the "action" - DomNodeList formResults = idpPage.getElementsByTagName("form"); - Assert.assertFalse(formResults.isEmpty()); - - DomElement formResult = formResults.get(0); - String action = formResult.getAttributeNS(null, "action"); - Assert.assertTrue(action.equals(consumerURL)); - - // Decode + verify response - byte[] deflatedToken = Base64Utility.decode(samlResponse); - InputStream inputStream = new DeflateEncoderDecoder().inflateToken(deflatedToken); - - Document responseDoc = StaxUtils.read(new InputStreamReader(inputStream, "UTF-8")); - - XMLObject responseObject = OpenSAMLUtil.fromDom(responseDoc.getDocumentElement()); - Assert.assertTrue(responseObject instanceof org.opensaml.saml.saml2.core.Response); - - org.opensaml.saml.saml2.core.Response samlResponseObject = - (org.opensaml.saml.saml2.core.Response)responseObject; - Assert.assertTrue(authnRequest.getID().equals(samlResponseObject.getInResponseTo())); + org.opensaml.saml.saml2.core.Response samlResponse = + parseSAMLResponse(idpPage, relayState, consumerURL, authnRequest.getID()); + String expected = "urn:oasis:names:tc:SAML:2.0:status:Success"; + Assert.assertEquals(expected, samlResponse.getStatus().getStatusCode().getValue()); // Check claims - String parsedResponse = DOM2Writer.nodeToString(responseDoc); + String parsedResponse = DOM2Writer.nodeToString(samlResponse.getDOM().getOwnerDocument()); String claim = ClaimTypes.FIRSTNAME.toString(); Assert.assertTrue(parsedResponse.contains(claim)); claim = ClaimTypes.LASTNAME.toString(); @@ -544,12 +477,12 @@ public class IdpTest { new UsernamePasswordCredentials(user, password)); webClient.getOptions().setJavaScriptEnabled(false); - try { - webClient.getPage(url); - Assert.fail("Failure expected on a bad RACS URL"); - } catch (FailingHttpStatusCodeException ex) { - Assert.assertEquals(ex.getStatusCode(), 400); - } + final HtmlPage idpPage = webClient.getPage(url); + + org.opensaml.saml.saml2.core.Response samlResponse = + parseSAMLResponse(idpPage, relayState, consumerURL, authnRequest.getID()); + String expected = "urn:oasis:names:tc:SAML:2.0:status:Requester"; + Assert.assertEquals(expected, samlResponse.getStatus().getStatusCode().getValue()); webClient.close(); } @@ -601,4 +534,52 @@ public class IdpTest { signableObject.releaseChildrenDOM(true); } + + private org.opensaml.saml.saml2.core.Response parseSAMLResponse(HtmlPage idpPage, + String relayState, + String consumerURL, + String authnRequestId + ) throws Exception { + Assert.assertEquals("IDP SignIn Response Form", idpPage.getTitleText()); + + // Parse the form to get the token (SAMLResponse) + DomNodeList results = idpPage.getElementsByTagName("input"); + + String samlResponse = null; + boolean foundRelayState = false; + for (DomElement result : results) { + if ("SAMLResponse".equals(result.getAttributeNS(null, "name"))) { + samlResponse = result.getAttributeNS(null, "value"); + } else if ("RelayState".equals(result.getAttributeNS(null, "name"))) { + foundRelayState = true; + Assert.assertEquals(result.getAttributeNS(null, "value"), relayState); + } + } + + Assert.assertNotNull(samlResponse); + Assert.assertTrue(foundRelayState); + + // Check the "action" + DomNodeList formResults = idpPage.getElementsByTagName("form"); + Assert.assertFalse(formResults.isEmpty()); + + DomElement formResult = formResults.get(0); + String action = formResult.getAttributeNS(null, "action"); + Assert.assertTrue(action.equals(consumerURL)); + + // Decode + verify response + byte[] deflatedToken = Base64Utility.decode(samlResponse); + InputStream inputStream = new DeflateEncoderDecoder().inflateToken(deflatedToken); + + Document responseDoc = StaxUtils.read(new InputStreamReader(inputStream, "UTF-8")); + + XMLObject responseObject = OpenSAMLUtil.fromDom(responseDoc.getDocumentElement()); + Assert.assertTrue(responseObject instanceof org.opensaml.saml.saml2.core.Response); + + org.opensaml.saml.saml2.core.Response samlResponseObject = + (org.opensaml.saml.saml2.core.Response)responseObject; + Assert.assertTrue(authnRequestId.equals(samlResponseObject.getInResponseTo())); + + return samlResponseObject; + } }