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/4808a7b4
Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/4808a7b4
Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/4808a7b4
Branch: refs/heads/master
Commit: 4808a7b49a7948e459c57d7ba1d228ea873cdcd7
Parents: 110cac0
Author: Colm O hEigeartaigh <coheigea@apache.org>
Authored: Wed Aug 9 12:41:34 2017 +0100
Committer: Colm O hEigeartaigh <coheigea@apache.org>
Committed: Wed Aug 9 12:41:34 2017 +0100
----------------------------------------------------------------------
.../cxf/fediz/service/idp/MetadataServlet.java | 9 +-
.../service/idp/metadata/IdpMetadataWriter.java | 89 +++++++++++++++++---
.../cxf/fediz/systests/samlsso/IdpTest.java | 38 +++++++++
3 files changed, 121 insertions(+), 15 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/4808a7b4/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/4808a7b4/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/4808a7b4/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/samlsso/IdpTest.java
----------------------------------------------------------------------
diff --git a/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/samlsso/IdpTest.java
b/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/samlsso/IdpTest.java
index 6542eed..d0fa7b9 100644
--- a/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/samlsso/IdpTest.java
+++ b/systests/samlsso/src/test/java/org/apache/cxf/fediz/systests/samlsso/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;
@@ -216,6 +220,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();
|