keycloak-developers

KEYCLOAK-3268

7/27/2016 10:28:48 AM

Details

diff --git a/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java b/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java
index 4d83d39..e6c10af 100755
--- a/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java
+++ b/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java
@@ -26,24 +26,25 @@ public class SPMetadataDescriptor {
         String descriptor =
                 "<EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"" + entityId + "\">\n" +
                 "    <SPSSODescriptor AuthnRequestsSigned=\"" + wantAuthnRequestsSigned + "\"\n" +
-                "            protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol http://schemas.xmlsoap.org/ws/2003/07/secext\">\n" +
+                "            protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol http://schemas.xmlsoap.org/ws/2003/07/secext\">\n";
+        if (wantAuthnRequestsSigned) {
+            descriptor +=
+                    "        <KeyDescriptor use=\"signing\">\n" +
+                            "            <dsig:KeyInfo xmlns:dsig=\"http://www.w3.org/2000/09/xmldsig#\">\n" +
+                            "                <dsig:X509Data>\n" +
+                            "                    <dsig:X509Certificate>\n" + certificatePem + "\n" +
+                            "                    </dsig:X509Certificate>\n" +
+                            "                </dsig:X509Data>\n" +
+                            "            </dsig:KeyInfo>\n" +
+                            "        </KeyDescriptor>\n";
+        }
+        descriptor +=
                 "        <SingleLogoutService Binding=\"" + binding + "\" Location=\"" + logoutEndpoint + "\"/>\n" +
                 "        <NameIDFormat>" + nameIDPolicyFormat + "\n" +
                 "        </NameIDFormat>\n" +
                 "        <AssertionConsumerService\n" +
                 "                Binding=\"" + binding + "\" Location=\"" + assertionEndpoint + "\"\n" +
                 "                index=\"1\" isDefault=\"true\" />\n";
-        if (wantAuthnRequestsSigned) {
-            descriptor +=
-                "        <KeyDescriptor use=\"signing\">\n" +
-                "            <dsig:KeyInfo xmlns:dsig=\"http://www.w3.org/2000/09/xmldsig#\">\n" +
-                "                <dsig:X509Data>\n" +
-                "                    <dsig:X509Certificate>\n" + certificatePem + "\n" +
-                "                    </dsig:X509Certificate>\n" +
-                "                </dsig:X509Data>\n" +
-                "            </dsig:KeyInfo>\n" +
-                "        </KeyDescriptor>\n";
-        }
         descriptor +=
                 "    </SPSSODescriptor>\n" +
                 "</EntityDescriptor>\n";
diff --git a/services/src/main/resources/idp-metadata-template.xml b/services/src/main/resources/idp-metadata-template.xml
index 8e4bdb8..6b3d5ec 100755
--- a/services/src/main/resources/idp-metadata-template.xml
+++ b/services/src/main/resources/idp-metadata-template.xml
@@ -18,15 +18,30 @@
 
 <EntitiesDescriptor Name="urn:keycloak"
 	xmlns="urn:oasis:names:tc:SAML:2.0:metadata"
-	xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+	xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 	<EntityDescriptor entityID="${idp.entityID}">
 		<IDPSSODescriptor WantAuthnRequestsSigned="true"
 			protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
-            <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
-            <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
+
+			<KeyDescriptor use="signing">
+				 <dsig:KeyInfo>
+					 <dsig:X509Data>
+						 <dsig:X509Certificate>
+							 ${idp.signing.certificate}
+						 </dsig:X509Certificate>
+					 </dsig:X509Data>
+				 </dsig:KeyInfo>
+			</KeyDescriptor>
+			<SingleLogoutService
+					Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
+					Location="${idp.sls.HTTP-POST}" />
+			<SingleLogoutService
+					Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
+					Location="${idp.sso.HTTP-Redirect}" />
+			<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>
+			<NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>
 			<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat>
 			<NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>
-
 			<SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
 				Location="${idp.sso.HTTP-POST}" />
 			<SingleSignOnService
@@ -35,21 +50,6 @@
 			<SingleSignOnService
 				Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP"
 				Location="${idp.sso.HTTP-POST}" />
-			<SingleLogoutService
-					Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
-					Location="${idp.sls.HTTP-POST}" />
-			<SingleLogoutService
-					Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
-					Location="${idp.sso.HTTP-Redirect}" />
-            <KeyDescriptor use="signing">
-                <dsig:KeyInfo xmlns:dsig="http://www.w3.org/2000/09/xmldsig#">
-                    <dsig:X509Data>
-                        <dsig:X509Certificate>
-                            ${idp.signing.certificate}
-                        </dsig:X509Certificate>
-                    </dsig:X509Data>
-                </dsig:KeyInfo>
-            </KeyDescriptor>
 		</IDPSSODescriptor>
 	</EntityDescriptor>
 </EntitiesDescriptor>
\ No newline at end of file
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 0e3c17c..2183e12 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -245,6 +245,11 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.picketlink</groupId>
+            <artifactId>picketlink-federation</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.wildfly</groupId>
             <artifactId>wildfly-undertow</artifactId>
             <scope>test</scope>
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/ValidationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/ValidationTest.java
new file mode 100644
index 0000000..290bdae
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/ValidationTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed 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.keycloak.testsuite.saml;
+
+import org.junit.Ignore;
+import org.junit.Test;
+import org.keycloak.common.util.PemUtils;
+import org.keycloak.common.util.StreamUtil;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.saml.SamlProtocol;
+import org.keycloak.protocol.saml.SamlService;
+import org.keycloak.saml.SPMetadataDescriptor;
+import org.keycloak.services.resources.RealmsResource;
+import org.xml.sax.SAXException;
+
+import javax.ws.rs.core.UriInfo;
+import javax.xml.XMLConstants;
+import javax.xml.transform.Source;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.validation.Schema;
+import javax.xml.validation.SchemaFactory;
+import javax.xml.validation.Validator;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ValidationTest {
+
+    public static String getIDPMetadataDescriptor() throws IOException {
+        InputStream is = SamlService.class.getResourceAsStream("/idp-metadata-template.xml");
+        String template = StreamUtil.readString(is);
+        template = template.replace("${idp.entityID}", "http://keycloak.org/auth/realms/test");
+        template = template.replace("${idp.sso.HTTP-POST}", "http://keycloak.org/auth/realms/test/saml");
+        template = template.replace("${idp.sso.HTTP-Redirect}", "http://keycloak.org/auth/realms/test/saml");
+        template = template.replace("${idp.sls.HTTP-POST}", "http://keycloak.org/auth/realms/test/saml");
+        template = template.replace("${idp.signing.certificate}", KeycloakModelUtils.generateKeyPairCertificate("test").getCertificate());
+        return template;
+    }
+
+
+    @Test
+    @Ignore // ignore because it goes out to web
+    public void testIDPDescriptor() throws Exception {
+        URL schemaFile = getClass().getResource("/schema/saml/v2/saml-schema-metadata-2.0.xsd");
+        Source xmlFile = new StreamSource(new ByteArrayInputStream(getIDPMetadataDescriptor().getBytes()), "IDPSSODescriptor");
+        SchemaFactory schemaFactory = SchemaFactory
+                .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+        Schema schema = schemaFactory.newSchema(schemaFile);
+        Validator validator = schema.newValidator();
+        try {
+            validator.validate(xmlFile);
+            System.out.println(xmlFile.getSystemId() + " is valid");
+        } catch (SAXException e) {
+            System.out.println(xmlFile.getSystemId() + " is NOT valid");
+            System.out.println("Reason: " + e.getLocalizedMessage());
+        }
+    }
+    @Test
+    @Ignore // ignore because it goes out to web
+    public void testBrokerExportDescriptor() throws Exception {
+        URL schemaFile = getClass().getResource("/schema/saml/v2/saml-schema-metadata-2.0.xsd");
+        Source xmlFile = new StreamSource(new ByteArrayInputStream(SPMetadataDescriptor.getSPDescriptor(
+                "POST", "http://realm/assertion", "http://realm/logout", true, "test", SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT, KeycloakModelUtils.generateKeyPairCertificate("test").getCertificate()
+        ).getBytes()), "SP Descriptor");
+        SchemaFactory schemaFactory = SchemaFactory
+                .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
+        Schema schema = schemaFactory.newSchema(schemaFile);
+        Validator validator = schema.newValidator();
+        try {
+            validator.validate(xmlFile);
+            System.out.println(xmlFile.getSystemId() + " is valid");
+        } catch (SAXException e) {
+            System.out.println(xmlFile.getSystemId() + " is NOT valid");
+            System.out.println("Reason: " + e.getLocalizedMessage());
+        }
+    }
+}