FEDIZ-205 - Support creating IdP Metadata for SAML SSO

Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/ea3124e2
Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/ea3124e2
Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/ea3124e2

Branch: refs/heads/1.4.x-fixes
Commit: ea3124e252f649631507bfbfe187b41428e5fc99
Parents: ee592a7
Author: Colm O hEigeartaigh <cohei...@apache.org>
Authored: Wed Aug 9 12:41:34 2017 +0100
Committer: Colm O hEigeartaigh <cohei...@apache.org>
Committed: Wed Aug 9 15:29:02 2017 +0100

----------------------------------------------------------------------
 .../cxf/fediz/service/idp/MetadataServlet.java  |  9 +-
 .../service/idp/metadata/IdpMetadataWriter.java | 89 +++++++++++++++++---
 .../apache/cxf/fediz/systests/idp/IdpTest.java  | 38 +++++++++
 3 files changed, 121 insertions(+), 15 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/ea3124e2/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/MetadataServlet.java
----------------------------------------------------------------------
diff --git 
a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/MetadataServlet.java
 
b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/MetadataServlet.java
index 1077f8b..f09bd08 100644
--- 
a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/MetadataServlet.java
+++ 
b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/MetadataServlet.java
@@ -52,7 +52,6 @@ public class MetadataServlet extends HttpServlet {
     private ApplicationContext applicationContext;
     private String realm;
 
-
     @Override
     protected void doGet(HttpServletRequest request, HttpServletResponse 
response) throws ServletException,
         IOException {
@@ -62,6 +61,8 @@ public class MetadataServlet extends HttpServlet {
         ConfigService cs = 
(ConfigService)getApplicationContext().getBean("config");
         Idp idpConfig = cs.getIDP(realm);
         try {
+            boolean isSamlRequest = request.getQueryString() != null
+                && request.getQueryString().contains("protocol=saml");
             if (request.getServletPath() != null && 
request.getServletPath().startsWith("/metadata")) {
                 String parsedRealm =
                     
request.getRequestURI().substring(request.getRequestURI().indexOf("/metadata")
@@ -73,7 +74,7 @@ public class MetadataServlet extends HttpServlet {
                 // Default to writing out the metadata for the IdP
                 if (idpConfig.getRealm().equals(parsedRealm) || parsedRealm == 
null || parsedRealm.isEmpty()) {
                     IdpMetadataWriter mw = new IdpMetadataWriter();
-                    Document metadata = mw.getMetaData(idpConfig);
+                    Document metadata = mw.getMetaData(idpConfig, 
isSamlRequest);
                     out.write(DOM2Writer.nodeToString(metadata));
                     return;
                 }
@@ -92,7 +93,7 @@ public class MetadataServlet extends HttpServlet {
                 // Otherwise return the Metadata for the Idp
                 LOG.debug(idpConfig.toString());
                 IdpMetadataWriter mw = new IdpMetadataWriter();
-                Document metadata = mw.getMetaData(idpConfig);
+                Document metadata = mw.getMetaData(idpConfig, isSamlRequest);
                 out.write(DOM2Writer.nodeToString(metadata));
             }
         } catch (Exception ex) {
@@ -118,4 +119,6 @@ public class MetadataServlet extends HttpServlet {
         return applicationContext;
     }
 
+
+
 }

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/ea3124e2/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/metadata/IdpMetadataWriter.java
----------------------------------------------------------------------
diff --git 
a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/metadata/IdpMetadataWriter.java
 
b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/metadata/IdpMetadataWriter.java
index 97bcfcb..44eb6cb 100644
--- 
a/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/metadata/IdpMetadataWriter.java
+++ 
b/services/idp-core/src/main/java/org/apache/cxf/fediz/service/idp/metadata/IdpMetadataWriter.java
@@ -46,8 +46,11 @@ public class IdpMetadataWriter {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(IdpMetadataWriter.class);
 
-    //CHECKSTYLE:OFF
-    public Document getMetaData(Idp config) throws RuntimeException {
+    public Document getMetaData(Idp config) {
+        return getMetaData(config, false);
+    }
+
+    public Document getMetaData(Idp config, boolean saml) {
         try {
             //Return as text/xml
             Crypto crypto = 
CertsUtils.getCryptoFromFile(config.getCertificate());
@@ -63,12 +66,13 @@ public class IdpMetadataWriter {
             writer.writeAttribute("entityID", config.getIdpUrl().toString());
 
             writer.writeNamespace("md", SAML2_METADATA_NS);
-            writer.writeNamespace("fed", WS_FEDERATION_NS);
-            writer.writeNamespace("wsa", WS_ADDRESSING_NS);
-            writer.writeNamespace("auth", WS_FEDERATION_NS);
             writer.writeNamespace("xsi", SCHEMA_INSTANCE_NS);
 
-            writeFederationMetadata(writer, config, crypto);
+            if (saml) {
+                writeSAMLSSOMetadata(writer, config, crypto);
+            } else {
+                writeFederationMetadata(writer, config, crypto);
+            }
 
             writer.writeEndElement(); // EntityDescriptor
 
@@ -101,13 +105,17 @@ public class IdpMetadataWriter {
         XMLStreamWriter writer, Idp config, Crypto crypto
     ) throws XMLStreamException {
 
+        writer.writeNamespace("fed", WS_FEDERATION_NS);
+        writer.writeNamespace("wsa", WS_ADDRESSING_NS);
+        writer.writeNamespace("auth", WS_FEDERATION_NS);
+
         writer.writeStartElement("md", "RoleDescriptor", WS_FEDERATION_NS);
         writer.writeAttribute(SCHEMA_INSTANCE_NS, "type", 
"fed:SecurityTokenServiceType");
         writer.writeAttribute("protocolSupportEnumeration", WS_FEDERATION_NS);
-        if (config.getServiceDescription() != null && 
config.getServiceDescription().length() > 0 ) {
+        if (config.getServiceDescription() != null && 
config.getServiceDescription().length() > 0) {
             writer.writeAttribute("ServiceDescription", 
config.getServiceDescription());
         }
-        if (config.getServiceDisplayName() != null && 
config.getServiceDisplayName().length() > 0 ) {
+        if (config.getServiceDisplayName() != null && 
config.getServiceDisplayName().length() > 0) {
             writer.writeAttribute("ServiceDisplayName", 
config.getServiceDisplayName());
         }
 
@@ -115,11 +123,12 @@ public class IdpMetadataWriter {
         //missing organization, contactperson
 
         //KeyDescriptor
-        writer.writeStartElement("", "KeyDescriptor", SAML2_METADATA_NS);
+        writer.writeStartElement("md", "KeyDescriptor", SAML2_METADATA_NS);
         writer.writeAttribute("use", "signing");
-        writer.writeStartElement("", "KeyInfo", 
"http://www.w3.org/2000/09/xmldsig#";);
-        writer.writeStartElement("", "X509Data", 
"http://www.w3.org/2000/09/xmldsig#";);
-        writer.writeStartElement("", "X509Certificate", 
"http://www.w3.org/2000/09/xmldsig#";);
+        writer.writeStartElement("ds", "KeyInfo", 
"http://www.w3.org/2000/09/xmldsig#";);
+        writer.writeNamespace("ds", "http://www.w3.org/2000/09/xmldsig#";);
+        writer.writeStartElement("ds", "X509Data", 
"http://www.w3.org/2000/09/xmldsig#";);
+        writer.writeStartElement("ds", "X509Certificate", 
"http://www.w3.org/2000/09/xmldsig#";);
 
         try {
             String keyAlias = crypto.getDefaultX509Identifier();
@@ -176,5 +185,61 @@ public class IdpMetadataWriter {
         writer.writeEndElement(); // RoleDescriptor
     }
 
+    private void writeSAMLSSOMetadata(
+        XMLStreamWriter writer, Idp config, Crypto crypto
+    ) throws XMLStreamException {
+
+        writer.writeStartElement("md", "IDPSSODescriptor", SAML2_METADATA_NS);
+        writer.writeAttribute("WantAuthnRequestsSigned", "true");
+        writer.writeAttribute("protocolSupportEnumeration", 
"urn:oasis:names:tc:SAML:2.0:protocol");
+
+        //KeyDescriptor
+        writer.writeStartElement("md", "KeyDescriptor", SAML2_METADATA_NS);
+        writer.writeAttribute("use", "signing");
+        writer.writeStartElement("ds", "KeyInfo", 
"http://www.w3.org/2000/09/xmldsig#";);
+        writer.writeNamespace("ds", "http://www.w3.org/2000/09/xmldsig#";);
+        writer.writeStartElement("ds", "X509Data", 
"http://www.w3.org/2000/09/xmldsig#";);
+        writer.writeStartElement("ds", "X509Certificate", 
"http://www.w3.org/2000/09/xmldsig#";);
+
+        try {
+            String keyAlias = crypto.getDefaultX509Identifier();
+            X509Certificate cert = 
CertsUtils.getX509CertificateFromCrypto(crypto, keyAlias);
+            writer.writeCharacters(Base64.encode(cert.getEncoded()));
+        } catch (Exception ex) {
+            LOG.error("Failed to add certificate information to metadata. 
Metadata incomplete", ex);
+        }
+
+        writer.writeEndElement(); // X509Certificate
+        writer.writeEndElement(); // X509Data
+        writer.writeEndElement(); // KeyInfo
+        writer.writeEndElement(); // KeyDescriptor
+
+
+        writer.writeStartElement("md", "NameIDFormat", SAML2_METADATA_NS);
+        
writer.writeCharacters("urn:oasis:names:tc:SAML:2.0:nameid-format:persistent");
+        writer.writeEndElement(); // NameIDFormat
+
+        writer.writeStartElement("md", "NameIDFormat", SAML2_METADATA_NS);
+        
writer.writeCharacters("urn:oasis:names:tc:SAML:2.0:nameid-format:unspecified");
+        writer.writeEndElement(); // NameIDFormat
+
+        writer.writeStartElement("md", "NameIDFormat", SAML2_METADATA_NS);
+        
writer.writeCharacters("urn:oasis:names:tc:SAML:2.0:nameid-format:emailAddress");
+        writer.writeEndElement(); // NameIDFormat
+
+        // SingleSignOnService
+        writer.writeStartElement("md", "SingleSignOnService", 
SAML2_METADATA_NS);
+        writer.writeAttribute("Binding", 
"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect");
+        writer.writeAttribute("Location", config.getIdpUrl().toString());
+        writer.writeEndElement(); // SingleSignOnService
+
+        // SingleSignOnService
+        writer.writeStartElement("md", "SingleSignOnService", 
SAML2_METADATA_NS);
+        writer.writeAttribute("Binding", 
"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
+        writer.writeAttribute("Location", config.getIdpUrl().toString());
+        writer.writeEndElement(); // SingleSignOnService
+
+        writer.writeEndElement(); // IDPSSODescriptor
+    }
 
 }

http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/ea3124e2/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 9e0a4f9..5358318 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
@@ -37,6 +37,7 @@ import javax.servlet.ServletException;
 
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
+import org.w3c.dom.Node;
 
 import com.gargoylesoftware.htmlunit.CookieManager;
 import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
@@ -47,6 +48,7 @@ import com.gargoylesoftware.htmlunit.html.DomElement;
 import com.gargoylesoftware.htmlunit.html.DomNodeList;
 import com.gargoylesoftware.htmlunit.html.HtmlPage;
 import com.gargoylesoftware.htmlunit.util.NameValuePair;
+import com.gargoylesoftware.htmlunit.xml.XmlPage;
 
 import org.apache.catalina.LifecycleException;
 import org.apache.catalina.LifecycleState;
@@ -68,10 +70,12 @@ import org.apache.wss4j.common.crypto.CryptoType;
 import org.apache.wss4j.common.saml.OpenSAMLUtil;
 import org.apache.wss4j.common.util.DOM2Writer;
 import org.apache.wss4j.dom.engine.WSSConfig;
+import org.apache.xml.security.signature.XMLSignature;
 import org.apache.xml.security.utils.Base64;
 import org.junit.AfterClass;
 import org.junit.Assert;
 import org.junit.BeforeClass;
+import org.junit.Test;
 import org.opensaml.core.xml.XMLObject;
 import org.opensaml.saml.common.SAMLVersion;
 import org.opensaml.saml.common.SignableSAMLObject;
@@ -225,6 +229,40 @@ public class IdpTest {
 
     }
     */
+
+    @Test
+    public void testIdPMetadata() throws Exception {
+        String url = "https://localhost:"; + getIdpHttpsPort()
+            + "/fediz-idp/metadata?protocol=saml";
+
+        final WebClient webClient = new WebClient();
+        webClient.getOptions().setUseInsecureSSL(true);
+        webClient.getOptions().setSSLClientCertificate(
+            this.getClass().getClassLoader().getResource("client.jks"), 
"storepass", "jks");
+
+        final XmlPage rpPage = webClient.getPage(url);
+        final String xmlContent = rpPage.asXml();
+        Assert.assertTrue(xmlContent.startsWith("<md:EntityDescriptor"));
+
+        // Now validate the Signature
+        Document doc = rpPage.getXmlDocument();
+
+        doc.getDocumentElement().setIdAttributeNS(null, "ID", true);
+
+        Node signatureNode =
+            DOMUtils.getChild(doc.getDocumentElement(), "Signature");
+        Assert.assertNotNull(signatureNode);
+
+        XMLSignature signature = new XMLSignature((Element)signatureNode, "");
+        org.apache.xml.security.keys.KeyInfo ki = signature.getKeyInfo();
+        Assert.assertNotNull(ki);
+        Assert.assertNotNull(ki.getX509Certificate());
+
+        
Assert.assertTrue(signature.checkSignatureValue(ki.getX509Certificate()));
+
+        webClient.close();
+    }
+
     @org.junit.Test
     public void testSuccessfulInvokeOnIdP() throws Exception {
         OpenSAMLUtil.initSamlEngine();

Reply via email to