[java-idp-testbed] 02/02: IDP-1008 - Add ability to send attribute queries

Tom Zeller tzeller at dragonacea.biz
Tue Aug 2 16:50:31 EDT 2016


This is an automated email from the git hooks/post-receive script.

tzeller pushed a commit to branch master
in repository java-idp-testbed.

View the commit online:
http://git.shibboleth.net/view/?p=java-idp-testbed.git;a=commit;h=3f304568ee58c59fac65066781f4e197292bb071

commit 3f304568ee58c59fac65066781f4e197292bb071
Author: Tom Zeller <tzeller at dragonacea.biz>
AuthorDate: Tue Aug 2 11:45:56 2016 -0500

    IDP-1008 - Add ability to send attribute queries
    
    Move opensaml-api test-jar dependency to compile scope from test scope.
    
    Add attribute query form to testbed home page.
    
    Add autowired Spring ApplicationContext to base Controller.
    
    Add SAML 2 attribute query support to testbed Controller.
    
    Bump testbed to 0.6 with addition of attribute query support.
    Since the integration tests use the testbed when running tests, it
    probably is a good idea to bump the testbed version when adding "high
    level features", but the "API" here is not clear anyway.
    
    https://issues.shibboleth.net/jira/browse/IDP-1008
---
 pom.xml                                  |  16 +--
 src/main/java/sp/BaseSAMLController.java |   5 +-
 src/main/java/sp/SAML2Controller.java    | 171 +++++++++++++++++++++++++++++++
 src/main/webapp/index.html               |  35 +++++++
 4 files changed, 218 insertions(+), 9 deletions(-)

diff --git a/pom.xml b/pom.xml
index 90d24ee..3189003 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
     <modelVersion>4.0.0</modelVersion>
     <groupId>net.shibboleth.idp</groupId>
     <artifactId>idp-testbed</artifactId>
-    <version>0.5.0-SNAPSHOT</version>
+    <version>0.6.0-SNAPSHOT</version>
     <packaging>war</packaging>
 
     <properties>
@@ -127,6 +127,13 @@
 
         <dependency>
             <groupId>${opensaml.groupId}</groupId>
+            <artifactId>opensaml-saml-api</artifactId>
+            <version>${opensaml.version}</version>
+            <type>test-jar</type>
+        </dependency>
+
+        <dependency>
+            <groupId>${opensaml.groupId}</groupId>
             <artifactId>opensaml-storage-impl</artifactId>
             <version>${opensaml.version}</version>
         </dependency>
@@ -278,13 +285,6 @@
                 </exclusion>
             </exclusions>
         </dependency>
-        <dependency>
-            <groupId>${opensaml.groupId}</groupId>
-            <artifactId>opensaml-saml-api</artifactId>
-            <version>${opensaml.version}</version>
-            <scope>test</scope>
-            <type>test-jar</type>
-        </dependency>
 
     </dependencies>
 
diff --git a/src/main/java/sp/BaseSAMLController.java b/src/main/java/sp/BaseSAMLController.java
index 9c13e9f..f0b47d7 100644
--- a/src/main/java/sp/BaseSAMLController.java
+++ b/src/main/java/sp/BaseSAMLController.java
@@ -37,6 +37,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.ApplicationContext;
 
 public abstract class BaseSAMLController {
 
@@ -53,7 +54,9 @@ public abstract class BaseSAMLController {
     @Autowired protected ParserPool parserPool;
 
     @Autowired @Qualifier("test.sp.Credential") protected Credential spCredential;
-
+    
+    @Autowired protected ApplicationContext applicationContext;
+    
     protected MessageContext<SAMLObject> decodeInboundMessageContextPost(HttpServletRequest servletRequest)
             throws Exception {
         HTTPPostDecoder decoder = new HTTPPostDecoder();
diff --git a/src/main/java/sp/SAML2Controller.java b/src/main/java/sp/SAML2Controller.java
index a716887..266d696 100644
--- a/src/main/java/sp/SAML2Controller.java
+++ b/src/main/java/sp/SAML2Controller.java
@@ -1,17 +1,34 @@
 package sp;
 
 import java.net.MalformedURLException;
+import java.security.KeyStore;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
 import java.util.Map;
 
+import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
+import javax.net.ssl.SSLContext;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
 import net.shibboleth.utilities.java.support.net.URLBuilder;
+import net.shibboleth.utilities.java.support.security.SecureRandomIdentifierGenerationStrategy;
 import net.shibboleth.utilities.java.support.xml.SerializeSupport;
 
+import org.apache.http.client.HttpClient;
+import org.apache.http.conn.ssl.SSLContextBuilder;
+import org.apache.http.conn.ssl.SSLContexts;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.cryptacular.util.CertUtil;
+import org.cryptacular.util.KeyPairUtil;
 import org.joda.time.DateTime;
+import org.opensaml.core.xml.XMLObject;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.messaging.context.InOutOperationContext;
 import org.opensaml.messaging.context.MessageContext;
 import org.opensaml.messaging.encoder.MessageEncodingException;
 import org.opensaml.saml.common.SAMLObject;
@@ -22,6 +39,7 @@ import org.opensaml.saml.common.xml.SAMLConstants;
 import org.opensaml.saml.ext.saml2aslo.Asynchronous;
 import org.opensaml.saml.saml2.binding.encoding.impl.HTTPPostEncoder;
 import org.opensaml.saml.saml2.binding.encoding.impl.HTTPRedirectDeflateEncoder;
+import org.opensaml.saml.saml2.core.AttributeQuery;
 import org.opensaml.saml.saml2.core.AuthnContextClassRef;
 import org.opensaml.saml.saml2.core.AuthnRequest;
 import org.opensaml.saml.saml2.core.Extensions;
@@ -34,20 +52,28 @@ import org.opensaml.saml.saml2.core.RequestedAuthnContext;
 import org.opensaml.saml.saml2.core.Response;
 import org.opensaml.saml.saml2.core.Status;
 import org.opensaml.saml.saml2.core.StatusCode;
+import org.opensaml.saml.saml2.core.Subject;
 import org.opensaml.saml.saml2.metadata.Endpoint;
 import org.opensaml.saml.saml2.metadata.SingleLogoutService;
 import org.opensaml.saml.saml2.metadata.SingleSignOnService;
+import org.opensaml.saml.saml2.profile.SAML2ActionTestingSupport;
+import org.opensaml.soap.client.http.HttpSOAPClient;
+import org.opensaml.soap.messaging.context.SOAP11Context;
+import org.opensaml.soap.soap11.Body;
+import org.opensaml.soap.soap11.Envelope;
 import org.opensaml.xmlsec.SignatureSigningParameters;
 import org.opensaml.xmlsec.context.SecurityParametersContext;
 import org.opensaml.xmlsec.signature.support.SignatureConstants;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.core.io.Resource;
 import org.springframework.http.HttpHeaders;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
 import org.springframework.web.servlet.HandlerMapping;
 import org.w3c.dom.Element;
 
@@ -517,4 +543,149 @@ public class SAML2Controller extends BaseSAMLController {
         return new ResponseEntity<>(formattedMessage, headers, HttpStatus.OK);
     }
 
+    /**
+     * Send a SAML 2 attribute query.
+     * 
+     * @param servletRequest the servlet request
+     * @param servletResponse the servlet response
+     * @param endpoint the endpoint to send the attribute query to
+     * @param principalName the name of the principal to query for
+     * @param trustedCertificate the trusted IdP public certificate
+     * @param trustedCertificatePassword the IdP certificate password
+     * @param clientCertificate the SP public certificate
+     * @param clientKey the SP private key
+     * @param clientPassword the SP password
+     * @return the SAML 2 attribute query response is displayed
+     * @throws Exception if an error occurs
+     */
+    @RequestMapping(value = "/AttributeQuery", method = RequestMethod.POST) public ResponseEntity<String>
+            initSAML2AttributeQuery(HttpServletRequest servletRequest, HttpServletResponse servletResponse,
+                    @RequestParam("endpoint") String endpoint,
+                    @RequestParam("principalName") String principalName,
+                    @RequestParam("trustedCertificate") String trustedCertificate,
+                    @RequestParam("trustedCertificatePassword") String trustedCertificatePassword,
+                    @RequestParam("clientCertificate") String clientCertificate,
+                    @RequestParam("clientKey") String clientKey,
+                    @RequestParam("clientPassword") String clientPassword) throws Exception {
+
+        final Resource trustedCertificateResource = applicationContext.getResource(trustedCertificate);
+        log.debug("SAML 2 AttributeQuery trusted certificate  '{}'", trustedCertificateResource);
+
+        final Resource clientCertificateResource = applicationContext.getResource(clientCertificate);
+        log.debug("SAML 2 AttributeQuery client certificate  '{}'", clientCertificateResource);
+
+        final Resource clientKeyResource = applicationContext.getResource(clientKey);
+        log.debug("SAML 2 AttributeQuery client key  '{}'", clientKeyResource);
+
+        final HttpClient httpClient = buildHttpClient(trustedCertificateResource, trustedCertificatePassword,
+                clientCertificateResource, clientKeyResource, clientPassword);
+
+        final HttpSOAPClient httpSoapClient = new HttpSOAPClient();
+        httpSoapClient.setParserPool(parserPool);
+        httpSoapClient.setHttpClient(httpClient);
+
+        final AttributeQuery attributeQuery = buildSAML2AttributeQueryRequest(servletRequest, principalName);
+
+        final Envelope envelope = buildSOAP11Envelope(attributeQuery);
+
+        if (log.isDebugEnabled()) {
+            log.debug("Sending AttributeQuery to endpoint '{}':\n", endpoint, SerializeSupport.prettyPrintXML(
+                    marshallerFactory.getMarshaller(envelope).marshall(envelope, parserPool.newDocument())));
+        }
+
+        final InOutOperationContext context = buildInOutOperationContext(envelope);
+
+        httpSoapClient.send(endpoint, context);
+
+        final Envelope soapResponse =
+                context.getInboundMessageContext().getSubcontext(SOAP11Context.class).getEnvelope();
+
+        final String formattedMessage = SerializeSupport.prettyPrintXML(soapResponse.getDOM());
+
+        final HttpHeaders headers = new HttpHeaders();
+        headers.add("Content-Type", "text/plain");
+
+        return new ResponseEntity<>(formattedMessage, headers, HttpStatus.OK);
+    }
+
+    /**
+     * Builds a basic SAML 2 attribute query.
+     * 
+     * @param requester the requester
+     * @param principalName the principal name
+     * @return the attribute query
+     */
+    @Nonnull public AttributeQuery buildSAML2AttributeQueryRequest(@Nonnull final HttpServletRequest servletRequest,
+            @Nonnull final String principalName) {
+
+        final Subject subject = SAML2ActionTestingSupport.buildSubject(principalName);
+
+        final AttributeQuery attributeQuery = SAML2ActionTestingSupport.buildAttributeQueryRequest(subject);
+        attributeQuery.setIssueInstant(new DateTime());
+        attributeQuery.setID(new SecureRandomIdentifierGenerationStrategy().generateIdentifier());
+        attributeQuery.getIssuer().setValue(getSpEntityId(servletRequest));
+
+        // TODO AttributeDesignator
+
+        return attributeQuery;
+    }
+
+    /**
+     * Build a SOAP11 {@link Envelope} with the given payload.
+     * 
+     * @param payload the payload
+     * @return the SOAP11 envelop
+     */
+    @Nonnull public Envelope buildSOAP11Envelope(@Nonnull final XMLObject payload) {
+        final Envelope envelope = XMLObjectProviderRegistrySupport.getBuilderFactory()
+                .<Envelope> getBuilderOrThrow(Envelope.DEFAULT_ELEMENT_NAME).buildObject(Envelope.DEFAULT_ELEMENT_NAME);
+        final Body body = XMLObjectProviderRegistrySupport.getBuilderFactory()
+                .<Body> getBuilderOrThrow(Body.DEFAULT_ELEMENT_NAME).buildObject(Body.DEFAULT_ELEMENT_NAME);
+        body.getUnknownXMLObjects().add(payload);
+        envelope.setBody(body);
+        return envelope;
+    }
+
+    /**
+     * Build a {@link InOutOperationContext}.
+     * 
+     * @param envelope the envelope
+     * @return the context
+     */
+    @Nonnull public static InOutOperationContext buildInOutOperationContext(@Nonnull final Envelope envelope) {
+        final SOAP11Context soap11Ctx = new SOAP11Context();
+        soap11Ctx.setEnvelope(envelope);
+
+        final MessageContext msgCtx = new MessageContext();
+        msgCtx.addSubcontext(soap11Ctx);
+
+        final InOutOperationContext inOutOpCtx = new InOutOperationContext() {};
+        inOutOpCtx.setOutboundMessageContext(msgCtx);
+
+        return inOutOpCtx;
+    }
+
+    @Nonnull public HttpClient buildHttpClient(Resource trustedCertificate, String trustedCertificatePassword, Resource clientCertificate,
+            Resource clientKey, String clientPassword) throws Exception {
+
+        final KeyStore trustStore = KeyStore.getInstance("PKCS12");
+        trustStore.load(trustedCertificate.getInputStream(), trustedCertificatePassword.toCharArray());
+
+        final PrivateKey clientPrivateKey = KeyPairUtil.readPrivateKey(clientKey.getInputStream());
+        final X509Certificate clientCert = CertUtil.readCertificate(clientCertificate.getInputStream());
+        final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
+        keyStore.load(null, null);
+        keyStore.setKeyEntry("sp", clientPrivateKey, clientPassword.toCharArray(), new Certificate[] {clientCert});
+
+        final SSLContextBuilder sslContextBuilder = SSLContexts.custom();
+        sslContextBuilder.loadTrustMaterial(trustStore);
+        sslContextBuilder.loadKeyMaterial(keyStore, clientPassword.toCharArray());
+
+        final SSLContext sslcontext = sslContextBuilder.build();
+
+        final CloseableHttpClient httpClient = HttpClients.custom().setSslcontext(sslcontext).build();
+
+        return httpClient;
+    }
+
 }
diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html
index 8cffdd0..138135f 100644
--- a/src/main/webapp/index.html
+++ b/src/main/webapp/index.html
@@ -188,6 +188,41 @@
 </form>
 </li> 
 <li><a href="idp/profile/cas/login?service=https://localhost:8443/sp/CAS/Service">idp/profile/cas/login?service=foo</a></li>
+<li>SAML 2 AttributeQuery
+<form id="saml2-attribute-query" action="sp/SAML2/AttributeQuery" method="POST">
+    <table>
+        <tr>
+            <td>endpoint</td>
+            <td><input id="saml2-attribute-query-endpoint" name="endpoint" value="https://localhost:9443/idp/profile/SAML2/SOAP/AttributeQuery" size="60" /></td>
+        </tr>
+        <tr>
+            <td>principalName</td>
+            <td><input id="saml2-attribute-query-principalName" name="principalName" value="jdoe" /></td>
+        </tr>
+        <tr>
+            <td>trustedCertificate</td>
+            <td><input id="saml2-attribute-query-trustedCertificate" name="trustedCertificate" value="classpath:/credentials/idp-backchannel.p12" size="60" /></td>
+        </tr>
+        <tr>
+            <td>trustedCertificatePassword</td>
+            <td><input id="saml2-attribute-query-trustedCertificatePassword" name="trustedCertificatePassword" value="changeit" size="20" /></td>
+        </tr>
+        <tr>
+            <td>clientCertificate</td>
+            <td><input id="saml2-attribute-query-clientCertificate" name="clientCertificate" value="classpath:/credentials/sp.crt" size="60" /></td>
+        </tr>
+        <tr>
+            <td>clientKey</td>
+            <td><input id="saml2-attribute-query-clientKey" name="clientKey" value="classpath:/credentials/sp.key" size="60" /></td>
+        </tr>
+        <tr>
+            <td>clientPassword</td>
+            <td><input id="saml2-attribute-query-clientPassword" name="clientPassword" value="secret" size="20" /></td>
+        </tr>
+    </table>
+    <input type="submit" value="SAML2AttributeQuery">
+</form>
+</li>
 
 </ul>
 

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list