[picketlink-commits] Picketlink SVN: r389 - in federation/branches/Branch_1_x/picketlink-fed-core/src: main/java/org/picketlink/identity/federation/core/wstrust/wrappers and 2 other directories.

picketlink-commits at lists.jboss.org picketlink-commits at lists.jboss.org
Mon Sep 6 21:27:16 EDT 2010


Author: sguilhen at redhat.com
Date: 2010-09-06 21:27:15 -0400 (Mon, 06 Sep 2010)
New Revision: 389

Modified:
   federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/PicketLinkSTS.java
   federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/StandardRequestHandler.java
   federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/WSTrustConstants.java
   federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/WSTrustJAXBFactory.java
   federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/WSTrustRequestHandler.java
   federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/wrappers/RequestSecurityToken.java
   federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/wrappers/RequestSecurityTokenCollection.java
   federation/branches/Branch_1_x/picketlink-fed-core/src/test/java/org/picketlink/test/identity/federation/core/saml/v2/X500AttributeUnitTestCase.java
   federation/branches/Branch_1_x/picketlink-fed-core/src/test/java/org/picketlink/test/identity/federation/core/wstrust/PicketLinkSTSUnitTestCase.java
Log:
PLFED-17: Added support for WS-Trust batch requests

Modified: federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/PicketLinkSTS.java
===================================================================
--- federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/PicketLinkSTS.java	2010-08-25 21:17:40 UTC (rev 388)
+++ federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/PicketLinkSTS.java	2010-09-07 01:27:15 UTC (rev 389)
@@ -1,23 +1,19 @@
 /*
- * JBoss, Home of Professional Open Source.
- * Copyright 2009, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors. 
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ * JBoss, Home of Professional Open Source. Copyright 2009, Red Hat Middleware LLC, and individual contributors as
+ * indicated by the @author tags. See the copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any
+ * later version.
+ * 
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License along with this software; if not, write to
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF site:
+ * http://www.fsf.org.
  */
 package org.picketlink.identity.federation.core.wstrust;
 
@@ -38,7 +34,6 @@
 import org.apache.log4j.Logger;
 import org.picketlink.identity.federation.core.config.STSType;
 import org.picketlink.identity.federation.core.exceptions.ConfigurationException;
-import org.picketlink.identity.federation.core.saml.v2.common.SAMLDocumentHolder;
 import org.picketlink.identity.federation.core.wstrust.wrappers.BaseRequestSecurityToken;
 import org.picketlink.identity.federation.core.wstrust.wrappers.RequestSecurityToken;
 import org.picketlink.identity.federation.core.wstrust.wrappers.RequestSecurityTokenCollection;
@@ -55,7 +50,7 @@
  */
 @WebServiceProvider(serviceName = "PicketLinkSTS", portName = "PicketLinkSTSPort", targetNamespace = "urn:picketlink:identity-federation:sts", wsdlLocation = "WEB-INF/wsdl/PicketLinkSTS.wsdl")
 @ServiceMode(value = Service.Mode.PAYLOAD)
-public class PicketLinkSTS implements Provider<Source>//SecurityTokenService
+public class PicketLinkSTS implements Provider<Source>// SecurityTokenService
 {
    private static Logger logger = Logger.getLogger(PicketLinkSTS.class);
 
@@ -77,6 +72,20 @@
     */
    public Source invoke(Source request)
    {
+      if (this.config == null)
+      {
+         try
+         {
+            if (logger.isInfoEnabled())
+               logger.info("Loading STS configuration");
+            this.config = this.getConfiguration();
+         }
+         catch (ConfigurationException e)
+         {
+            throw new WebServiceException("Encountered configuration exception:", e);
+         }
+      }
+
       BaseRequestSecurityToken baseRequest;
       try
       {
@@ -106,25 +115,6 @@
     */
    protected Source handleTokenRequest(RequestSecurityToken request)
    {
-      SAMLDocumentHolder holder = WSTrustJAXBFactory.getInstance().getSAMLDocumentHolderOnThread();
-
-      /**
-       * The RST Document is very important for XML Signatures
-       */
-      request.setRSTDocument(holder.getSamlDocument());
-
-      if (this.config == null)
-         try
-         {
-            if (logger.isInfoEnabled())
-               logger.info("Loading STS configuration");
-            this.config = this.getConfiguration();
-         }
-         catch (ConfigurationException e)
-         {
-            throw new WebServiceException("Encountered configuration exception:", e);
-         }
-
       WSTrustRequestHandler handler = this.config.getRequestHandler();
       String requestType = request.getRequestType().toString();
       if (logger.isDebugEnabled())
@@ -141,7 +131,6 @@
          else if (requestType.equals(WSTrustConstants.RENEW_REQUEST))
          {
             Source source = this.marshallResponse(handler.renew(request, this.context.getUserPrincipal()));
-            // we need to sign/encrypt renewed tokens.
             Document document = handler.postProcess((Document) ((DOMSource) source).getNode(), request);
             return new DOMSource(document);
          }
@@ -168,7 +157,68 @@
     */
    protected Source handleTokenRequestCollection(RequestSecurityTokenCollection requestCollection)
    {
-      throw new UnsupportedOperationException();
+      String requestType = requestCollection.getRequestSecurityTokens().get(0).getRequestType().toString();
+      if (logger.isDebugEnabled())
+         logger.debug("STS received requests of type " + requestType);
+      WSTrustRequestHandler handler = this.config.getRequestHandler();
+
+      try
+      {
+         if (requestType.equals(WSTrustConstants.BATCH_ISSUE_REQUEST))
+         {
+            // process each request at a time.
+            RequestSecurityTokenResponseCollection responseCollection = new RequestSecurityTokenResponseCollection();
+            for (RequestSecurityToken request : requestCollection.getRequestSecurityTokens())
+               responseCollection.addRequestSecurityTokenResponse(handler.issue(request, this.context
+                     .getUserPrincipal()));
+            // marshal the whole response.
+            Source source = WSTrustJAXBFactory.getInstance().marshallRequestSecurityTokenResponse(responseCollection);
+            // post-process: sign and encrypt (if needed) all security tokens.
+            handler.postProcess((Document) ((DOMSource) source).getNode(), requestCollection.getRequestSecurityTokens()
+                  .toArray(new RequestSecurityToken[0]));
+            return source;
+         }
+         else if (requestType.equals(WSTrustConstants.BATCH_RENEW_REQUEST))
+         {
+            // process each request at a time.
+            RequestSecurityTokenResponseCollection responseCollection = new RequestSecurityTokenResponseCollection();
+            for (RequestSecurityToken request : requestCollection.getRequestSecurityTokens())
+               responseCollection.addRequestSecurityTokenResponse(handler.renew(request, this.context
+                     .getUserPrincipal()));
+            // marshal the whole response.
+            Source source = WSTrustJAXBFactory.getInstance().marshallRequestSecurityTokenResponse(responseCollection);
+            // post-process: sign and encrypt (if needed) all security tokens.
+            handler.postProcess((Document) ((DOMSource) source).getNode(), requestCollection.getRequestSecurityTokens()
+                  .toArray(new RequestSecurityToken[0]));
+            return source;
+         }
+         else if (requestType.equals(WSTrustConstants.BATCH_CANCEL_REQUEST))
+         {
+            // process each request at a time.
+            RequestSecurityTokenResponseCollection responseCollection = new RequestSecurityTokenResponseCollection();
+            for (RequestSecurityToken request : requestCollection.getRequestSecurityTokens())
+               responseCollection.addRequestSecurityTokenResponse(handler.cancel(request, this.context
+                     .getUserPrincipal()));
+            // marshal the whole response.
+            return WSTrustJAXBFactory.getInstance().marshallRequestSecurityTokenResponse(responseCollection);
+         }
+         else if (requestType.equals(WSTrustConstants.BATCH_VALIDATE_REQUEST))
+         {
+            // process each request at a time.
+            RequestSecurityTokenResponseCollection responseCollection = new RequestSecurityTokenResponseCollection();
+            for (RequestSecurityToken request : requestCollection.getRequestSecurityTokens())
+               responseCollection.addRequestSecurityTokenResponse(handler.validate(request, this.context
+                     .getUserPrincipal()));
+            // marshal the whole response.
+            return WSTrustJAXBFactory.getInstance().marshallRequestSecurityTokenResponse(responseCollection);
+         }
+         else
+            throw new WSTrustException("Invalid request type: " + requestType);
+      }
+      catch (WSTrustException we)
+      {
+         throw new WebServiceException("Exception in handling token request: " + we.getMessage(), we);
+      }
    }
 
    /**

Modified: federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/StandardRequestHandler.java
===================================================================
--- federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/StandardRequestHandler.java	2010-08-25 21:17:40 UTC (rev 388)
+++ federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/StandardRequestHandler.java	2010-09-07 01:27:15 UTC (rev 389)
@@ -1,23 +1,19 @@
 /*
- * JBoss, Home of Professional Open Source.
- * Copyright 2009, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors. 
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ * JBoss, Home of Professional Open Source. Copyright 2009, Red Hat Middleware LLC, and individual contributors as
+ * indicated by the @author tags. See the copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any
+ * later version.
+ * 
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License along with this software; if not, write to
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF site:
+ * http://www.fsf.org.
  */
 package org.picketlink.identity.federation.core.wstrust;
 
@@ -55,6 +51,7 @@
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
 
 /**
  * <p>
@@ -305,7 +302,8 @@
    public RequestSecurityTokenResponse renew(RequestSecurityToken request, Principal callerPrincipal)
          throws WSTrustException
    {
-      // first validate the provided token signature to make sure it has been issued by this STS and hasn't been tempered.
+      // first validate the provided token signature to make sure it has been issued by this STS and hasn't been
+      // tempered.
       if (trace)
          log.trace("Validating token for renew request " + request.getContext());
       if (request.getRenewTargetElement() == null)
@@ -352,6 +350,14 @@
             log.debug("Lifetime has not been specified. Using the default timeout value.");
          request.setLifetime(WSTrustUtil.createDefaultLifetime(this.configuration.getIssuedTokenTimeout()));
       }
+      long keySize = request.getKeySize();
+      if (keySize == 0)
+      {
+         if (log.isDebugEnabled())
+            log.debug("No key size could be found in the request. Using the default size. (" + KEY_SIZE + ")");
+         keySize = KEY_SIZE;
+         request.setKeySize(keySize);
+      }
 
       // create a context and dispatch to the proper security token provider for renewal.
       WSTrustRequestContext context = new WSTrustRequestContext(request, callerPrincipal);
@@ -523,96 +529,102 @@
       return response;
    }
 
-   public Document postProcess(Document rstrDocument, RequestSecurityToken request) throws WSTrustException
+   public Document postProcess(Document rstrDocument, RequestSecurityToken... requests) throws WSTrustException
    {
-      if (WSTrustConstants.ISSUE_REQUEST.equals(request.getRequestType().toString())
-            || WSTrustConstants.RENEW_REQUEST.equals(request.getRequestType().toString()))
+      rstrDocument = DocumentUtil.normalizeNamespaces(rstrDocument);
+
+      // Sign all security tokens found in the document.
+      if (this.configuration.signIssuedToken() && this.configuration.getSTSKeyPair() != null)
       {
-         rstrDocument = DocumentUtil.normalizeNamespaces(rstrDocument);
-
-         //Sign the security token
-         if (this.configuration.signIssuedToken() && this.configuration.getSTSKeyPair() != null)
+         KeyPair keyPair = this.configuration.getSTSKeyPair();
+         NodeList nodes = rstrDocument
+               .getElementsByTagNameNS(WSTrustConstants.BASE_NAMESPACE, "RequestedSecurityToken");
+         if (nodes != null)
          {
-            KeyPair keyPair = this.configuration.getSTSKeyPair();
-            URI signatureURI = request.getSignatureAlgorithm();
-            String signatureMethod = signatureURI != null ? signatureURI.toString() : SignatureMethod.RSA_SHA1;
-            try
+            for (int i = 0; i < nodes.getLength(); i++)
             {
-               Node rst = rstrDocument
-                     .getElementsByTagNameNS(WSTrustConstants.BASE_NAMESPACE, "RequestedSecurityToken").item(0);
-               Element tokenElement = (Element) rst.getFirstChild();
+               Node node = nodes.item(i);
+               Element tokenElement = (Element) node.getFirstChild();
                if (trace)
                   log.trace("NamespaceURI of element to be signed:" + tokenElement.getNamespaceURI());
 
-               //Set the CanonicalizationMethod if any
-               XMLSignatureUtil.setCanonicalizationMethodType( configuration.getXMLDSigCanonicalizationMethod() );
-               
-               rstrDocument = XMLSignatureUtil.sign(rstrDocument, tokenElement, keyPair, DigestMethod.SHA1,
-                     signatureMethod, "#" + tokenElement.getAttribute("ID"));
+               URI signatureURI = requests[i].getSignatureAlgorithm();
+               String signatureMethod = signatureURI != null ? signatureURI.toString() : SignatureMethod.RSA_SHA1;
+
+               // Set the CanonicalizationMethod if any
+               XMLSignatureUtil.setCanonicalizationMethodType(configuration.getXMLDSigCanonicalizationMethod());
+               try
+               {
+                  rstrDocument = XMLSignatureUtil.sign(rstrDocument, tokenElement, keyPair, DigestMethod.SHA1,
+                        signatureMethod, "#" + tokenElement.getAttribute("ID"));
+               }
+               catch (Exception e)
+               {
+                  throw new WSTrustException("Failed to sign security token", e);
+               }
+
                if (trace)
                {
                   try
                   {
                      log.trace("Signed Token:" + DocumentUtil.getNodeAsString(tokenElement));
-
                      Document tokenDocument = DocumentUtil.createDocument();
                      tokenDocument.appendChild(tokenDocument.importNode(tokenElement, true));
                      log.trace("valid=" + XMLSignatureUtil.validate(tokenDocument, keyPair.getPublic()));
-
                   }
                   catch (Exception ignore)
                   {
                   }
                }
             }
-            catch (Exception e)
-            {
-               throw new WSTrustException("Failed to sign security token", e);
-            }
          }
+      }
 
-         // encrypt the security token if needed.
-         if (this.configuration.encryptIssuedToken())
+      // encrypt all security tokens if needed.
+      if (this.configuration.encryptIssuedToken())
+      {
+         // encrypt the security tokens.
+         NodeList nodes = rstrDocument
+               .getElementsByTagNameNS(WSTrustConstants.BASE_NAMESPACE, "RequestedSecurityToken");
+         if (nodes != null)
          {
-            // get the public key that will be used to encrypt the token.
-            PublicKey providerPublicKey = null;
-            if (request.getAppliesTo() != null)
+            for (int i = 0; i < nodes.getLength(); i++)
             {
-               String serviceName = WSTrustUtil.parseAppliesTo(request.getAppliesTo());
-               if (trace)
-                  log.trace("Locating public key for service provider " + serviceName);
-               if (serviceName != null)
-                  providerPublicKey = this.configuration.getServiceProviderPublicKey(serviceName);
-            }
-            if (providerPublicKey == null)
-            {
-               log.warn("Security token should be encrypted but no encrypting key could be found");
-            }
-            else
-            {
-               // generate the secret key.
-               long keySize = request.getKeySize();
-               byte[] secret = WSTrustUtil.createRandomSecret((int) keySize / 8);
-               SecretKey secretKey = new SecretKeySpec(secret, "AES");
-
-               // encrypt the security token.
-               Node rst = rstrDocument
-                     .getElementsByTagNameNS(WSTrustConstants.BASE_NAMESPACE, "RequestedSecurityToken").item(0);
-               Element tokenElement = (Element) rst.getFirstChild();
-               try
+               // get the public key that will be used to encrypt the token.
+               PublicKey providerPublicKey = null;
+               if (requests[i].getAppliesTo() != null)
                {
-                  XMLEncryptionUtil.encryptElement(rstrDocument, tokenElement, providerPublicKey, secretKey,
-                        (int) keySize);
+                  String serviceName = WSTrustUtil.parseAppliesTo(requests[i].getAppliesTo());
+                  if (trace)
+                     log.trace("Locating public key for service provider " + serviceName);
+                  if (serviceName != null)
+                     providerPublicKey = this.configuration.getServiceProviderPublicKey(serviceName);
                }
-               catch (ProcessingException e)
+               if (providerPublicKey == null)
                {
-                  throw new WSTrustException("Unable to encrypt security token", e);
+                  log.warn("Security token should be encrypted but no encrypting key could be found");
                }
+               else
+               {
+                  // generate the secret key.
+                  long keySize = requests[i].getKeySize();
+                  byte[] secret = WSTrustUtil.createRandomSecret((int) keySize / 8);
+                  SecretKey secretKey = new SecretKeySpec(secret, "AES");
+
+                  Element tokenElement = (Element) nodes.item(i).getFirstChild();
+                  try
+                  {
+                     XMLEncryptionUtil.encryptElement(rstrDocument, tokenElement, providerPublicKey, secretKey,
+                           (int) keySize);
+                  }
+                  catch (ProcessingException e)
+                  {
+                     throw new WSTrustException("Unable to encrypt security token", e);
+                  }
+               }
             }
          }
       }
-
       return rstrDocument;
    }
-
 }
\ No newline at end of file

Modified: federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/WSTrustConstants.java
===================================================================
--- federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/WSTrustConstants.java	2010-08-25 21:17:40 UTC (rev 388)
+++ federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/WSTrustConstants.java	2010-09-07 01:27:15 UTC (rev 389)
@@ -39,6 +39,10 @@
    public static final String RENEW_REQUEST = BASE_NAMESPACE + "/Renew";
    public static final String CANCEL_REQUEST = BASE_NAMESPACE + "/Cancel";
    public static final String VALIDATE_REQUEST = BASE_NAMESPACE + "/Validate";
+   public static final String BATCH_ISSUE_REQUEST = BASE_NAMESPACE + "/BatchIssue";
+   public static final String BATCH_RENEW_REQUEST = BASE_NAMESPACE + "/BatchRenew";
+   public static final String BATCH_CANCEL_REQUEST = BASE_NAMESPACE + "/BatchCancel";
+   public static final String BATCH_VALIDATE_REQUEST = BASE_NAMESPACE + "/BatchValidate";
    
    // WS-Trust validation constants.
    public static final String STATUS_TYPE = BASE_NAMESPACE + "/RSTR/Status";

Modified: federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/WSTrustJAXBFactory.java
===================================================================
--- federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/WSTrustJAXBFactory.java	2010-08-25 21:17:40 UTC (rev 388)
+++ federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/WSTrustJAXBFactory.java	2010-09-07 01:27:15 UTC (rev 389)
@@ -1,31 +1,29 @@
 /*
- * JBoss, Home of Professional Open Source.
- * Copyright 2009, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors. 
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ * JBoss, Home of Professional Open Source. Copyright 2009, Red Hat Middleware LLC, and individual contributors as
+ * indicated by the @author tags. See the copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any
+ * later version.
+ * 
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License along with this software; if not, write to
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF site:
+ * http://www.fsf.org.
  */
 package org.picketlink.identity.federation.core.wstrust;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import javax.xml.bind.Binder;
 import javax.xml.bind.JAXBElement;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.Marshaller;
-import javax.xml.bind.Unmarshaller;
 import javax.xml.transform.Source;
 
 import org.apache.log4j.Logger;
@@ -35,9 +33,11 @@
 import org.picketlink.identity.federation.core.wstrust.wrappers.BaseRequestSecurityToken;
 import org.picketlink.identity.federation.core.wstrust.wrappers.BaseRequestSecurityTokenResponse;
 import org.picketlink.identity.federation.core.wstrust.wrappers.RequestSecurityToken;
+import org.picketlink.identity.federation.core.wstrust.wrappers.RequestSecurityTokenCollection;
 import org.picketlink.identity.federation.core.wstrust.wrappers.RequestSecurityTokenResponse;
 import org.picketlink.identity.federation.core.wstrust.wrappers.RequestSecurityTokenResponseCollection;
 import org.picketlink.identity.federation.ws.trust.ObjectFactory;
+import org.picketlink.identity.federation.ws.trust.RequestSecurityTokenCollectionType;
 import org.picketlink.identity.federation.ws.trust.RequestSecurityTokenResponseCollectionType;
 import org.picketlink.identity.federation.ws.trust.RequestSecurityTokenType;
 import org.w3c.dom.Document;
@@ -62,8 +62,6 @@
 
    private Marshaller marshaller;
 
-   private Unmarshaller unmarshaller;
-
    private Binder<Node> binder;
 
    private final ObjectFactory objectFactory;
@@ -80,7 +78,6 @@
       try
       {
          this.marshaller = JAXBUtil.getMarshaller(this.getPackages());
-         this.unmarshaller = JAXBUtil.getUnmarshaller(this.getPackages());
          this.binder = JAXBUtil.getJAXBContext(this.getPackages()).createBinder();
          this.objectFactory = new ObjectFactory();
       }
@@ -119,34 +116,39 @@
     * Creates a {@code BaseRequestSecurityToken} from the specified XML source.
     * </p>
     * 
-    * @param request
-    *           the XML source containing the security token request message.
+    * @param request the XML source containing the security token request message.
     * @return the constructed {@code BaseRequestSecurityToken} instance. It will be an instance of {@code
     *         RequestSecurityToken} the message contains a single token request, and an instance of {@code
     *         RequestSecurityTokenCollection} if multiples requests are being made in the same message.
     */
-   @SuppressWarnings("unchecked")
    public BaseRequestSecurityToken parseRequestSecurityToken(Source request) throws WSTrustException
    {
-      // if the request contains a validate, cancel, or renew target, we must preserve it from JAXB unmarshalling.
       try
       {
          Node documentNode = DocumentUtil.getNodeFromSource(request);
          Document document = documentNode instanceof Document ? (Document) documentNode : documentNode
                .getOwnerDocument();
-
-         JAXBElement<RequestSecurityTokenType> jaxbRST;
          Node rst = this.findNodeByNameNS(document, "RequestSecurityToken", WSTrustConstants.BASE_NAMESPACE);
          if (rst == null)
-            throw new RuntimeException("Request Security Token node not found");
+            throw new RuntimeException("The request document must contain at least one RequestSecurityToken node");
 
-         jaxbRST = (JAXBElement<RequestSecurityTokenType>) binder.unmarshal(rst);
-
-         RequestSecurityTokenType rstt = jaxbRST.getValue();
-
-         SAML2SecurityToken samlSecurityToken = new SAML2SecurityToken(rstt);
-         holders.set(new SAMLDocumentHolder(samlSecurityToken, document));
-         return new RequestSecurityToken(rstt);
+         JAXBElement<?> jaxbRST = (JAXBElement<?>) binder.unmarshal(document);
+         if (jaxbRST.getDeclaredType().equals(RequestSecurityTokenType.class))
+         {
+            RequestSecurityTokenType rstt = (RequestSecurityTokenType) jaxbRST.getValue();
+            RequestSecurityToken requestSecToken = new RequestSecurityToken(rstt);
+            requestSecToken.setRSTDocument(document);
+            return requestSecToken;
+         }
+         else if (jaxbRST.getDeclaredType().equals(RequestSecurityTokenCollectionType.class))
+         {
+            RequestSecurityTokenCollectionType rstct = (RequestSecurityTokenCollectionType) jaxbRST.getValue();
+            RequestSecurityTokenCollection requestSecTokenCollection = new RequestSecurityTokenCollection(rstct,
+                  document);
+            return requestSecTokenCollection;
+         }
+         else
+            throw new WSTrustException("Request message doesn't contain a valid request type");
       }
       catch (Exception e)
       {
@@ -159,16 +161,13 @@
     * Creates a {@code BaseRequestSecurityTokenResponse} from the specified XML source.
     * </p>
     * 
-    * @param response
-    *           the XML source containing the security token response message.
+    * @param response the XML source containing the security token response message.
     * @return the constructed {@code BaseRequestSecurityTokenResponse} instance. According to the WS-Trust
     *         specification, the returned object will be an instance of {@code RequestSecurityTokenResponseCollection}.
     */
    @SuppressWarnings("unchecked")
    public BaseRequestSecurityTokenResponse parseRequestSecurityTokenResponse(Source response) throws WSTrustException
    {
-      // if the response contains an issued token, we must preserve it from the JAXB unmarshalling.
-      Element tokenElement = null;
       Node documentNode = null;
       try
       {
@@ -179,15 +178,9 @@
          throw new WSTrustException("Failed to transform request source", e);
       }
 
-      Document document = documentNode instanceof Document ? (Document) documentNode : documentNode.getOwnerDocument();
-      Node requestedTokenNode = this.findNodeByNameNS(document, "RequestedSecurityToken",
-            WSTrustConstants.BASE_NAMESPACE);
-      if (requestedTokenNode != null)
-         tokenElement = (Element) requestedTokenNode.getFirstChild();
-
       try
       {
-         Object object = this.unmarshaller.unmarshal(documentNode);
+         Object object = this.binder.unmarshal(documentNode);
          if (object instanceof JAXBElement)
          {
             JAXBElement<?> element = (JAXBElement<?>) object;
@@ -195,12 +188,6 @@
             {
                RequestSecurityTokenResponseCollection collection = new RequestSecurityTokenResponseCollection(
                      (RequestSecurityTokenResponseCollectionType) element.getValue());
-               // insert the security token in the parsed response.
-               if (tokenElement != null)
-               {
-                  RequestSecurityTokenResponse parsedResponse = collection.getRequestSecurityTokenResponses().get(0);
-                  parsedResponse.getRequestedSecurityToken().setAny(tokenElement);
-               }
                return collection;
             }
             else
@@ -220,8 +207,7 @@
     * Creates a {@code javax.xml.transform.Source} from the specified request object.
     * </p>
     * 
-    * @param request
-    *           a {@code RequestSecurityToken} representing the object model of the security token request.
+    * @param request a {@code RequestSecurityToken} representing the object model of the security token request.
     * @return the constructed {@code Source} instance.
     */
    public Source marshallRequestSecurityToken(RequestSecurityToken request)
@@ -249,9 +235,9 @@
       try
       {
          result = DocumentUtil.createDocument();
-         this.marshaller.marshal(this.objectFactory.createRequestSecurityToken(request.getDelegate()), result);
+         this.binder.marshal(this.objectFactory.createRequestSecurityToken(request.getDelegate()), result);
 
-         // insert the original target in the appropriate element. 
+         // insert the original target in the appropriate element.
          if (targetElement != null)
          {
             Node node = null;
@@ -276,12 +262,101 @@
 
    /**
     * <p>
+    * Creates a {@code javax.xml.transform.Source} from the specified request object.
+    * </p>
+    * 
+    * @param request a {@code RequestSecurityTokenCollection} representing the object model of the security token batch
+    *           request.
+    * @return the constructed {@code Source} instance.
+    */
+   public Source marshallRequestSecurityTokenCollection(RequestSecurityTokenCollection collection)
+   {
+      // validation: the collection must contain at least one request.
+      if (collection == null || collection.getRequestSecurityTokens().size() == 0)
+         throw new IllegalArgumentException("The request collection must contain at least one request");
+
+      // validation: all requests must be the of the same type and must match one of the batch request types.
+      String requestType = null;
+      for (RequestSecurityToken request : collection.getRequestSecurityTokens())
+      {
+         // this is the first request: ensure its type is valid.
+         if (requestType == null)
+         {
+            if (request.getRequestType() == null || !isValidBatchRequestType(request.getRequestType().toString()))
+               throw new IllegalArgumentException(
+                     "The request type cannot be null and must be a valid WS-Trust batch request type");
+            requestType = request.getRequestType().toString();
+         }
+         // for the other requests, ensure their type matches the type of the first request.
+         else
+         {
+            if (request.getRequestType() == null || !requestType.equals(request.getRequestType().toString()))
+               throw new IllegalArgumentException("All requests must be of the same type. Invalid type: "
+                     + request.getRequestType());
+         }
+      }
+
+      // save the cancel/renew/validate targets to preserve them from the JAXB marshaling process.
+      List<Element> targets = new ArrayList<Element>();
+      for (RequestSecurityToken request : collection.getRequestSecurityTokens())
+      {
+         if (requestType.equals(WSTrustConstants.BATCH_CANCEL_REQUEST))
+         {
+            targets.add((Element) request.getCancelTarget().getAny());
+            request.getCancelTarget().setAny(null);
+         }
+         else if (requestType.equals(WSTrustConstants.BATCH_RENEW_REQUEST))
+         {
+            targets.add((Element) request.getRenewTarget().getAny());
+            request.getRenewTarget().setAny(null);
+         }
+         else if (requestType.equals(WSTrustConstants.BATCH_VALIDATE_REQUEST))
+         {
+            targets.add((Element) request.getValidateTarget().getAny());
+            request.getValidateTarget().setAny(null);
+         }
+      }
+
+      // marshal the document and reinsert the target elements in the generated XML document.
+      Document result = null;
+      try
+      {
+         result = DocumentUtil.createDocument();
+         this.binder.marshal(this.objectFactory.createRequestSecurityTokenCollection(collection.getDelegate()), result);
+
+         NodeList nodes = null;
+         if (requestType.equals(WSTrustConstants.BATCH_CANCEL_REQUEST))
+            nodes = result.getElementsByTagNameNS(WSTrustConstants.BASE_NAMESPACE, "CancelTarget");
+         else if (requestType.equals(WSTrustConstants.BATCH_RENEW_REQUEST))
+            nodes = result.getElementsByTagNameNS(WSTrustConstants.BASE_NAMESPACE, "RenewTarget");
+         else if (requestType.equals(WSTrustConstants.BATCH_VALIDATE_REQUEST))
+            nodes = result.getElementsByTagNameNS(WSTrustConstants.BASE_NAMESPACE, "ValidateTarget");
+
+         // iterate through the document nodes reinserting the original target elements.
+         if (nodes != null)
+         {
+            for (int i = 0; i < nodes.getLength(); i++)
+            {
+               Node node = nodes.item(i);
+               node.appendChild(result.importNode(targets.get(i), true));
+            }
+         }
+      }
+      catch (Exception e)
+      {
+         throw new RuntimeException("Failed to marshall security token request", e);
+      }
+
+      return DocumentUtil.getXMLSource(result);
+   }
+
+   /**
+    * <p>
     * Creates a {@code javax.xml.transform.Source} from the specified response object.
     * </p>
     * 
-    * @param collection
-    *           a {@code RequestSecurityTokenResponseCollection} representing the object model of the security token
-    *           response.
+    * @param collection a {@code RequestSecurityTokenResponseCollection} representing the object model of the security
+    *           token response.
     * @return the constructed {@code Source} instance.
     */
    public Source marshallRequestSecurityTokenResponse(RequestSecurityTokenResponseCollection collection)
@@ -289,29 +364,35 @@
       if (collection.getRequestSecurityTokenResponses().size() == 0)
          throw new IllegalArgumentException("The response collection must contain at least one response");
 
-      // if the response contains an issued token, we must preserve it from the JAXB marshaling.
-      Element tokenElement = null;
-      RequestSecurityTokenResponse response = collection.getRequestSecurityTokenResponses().get(0);
-      if (response.getRequestedSecurityToken() != null)
+      // if the response contains issued tokens, we must preserve them from the JAXB marshaling.
+      List<Element> tokenElements = new ArrayList<Element>();
+      for (RequestSecurityTokenResponse response : collection.getRequestSecurityTokenResponses())
       {
-         tokenElement = (Element) response.getRequestedSecurityToken().getAny();
-         // we don't want to marshall any token - it will be inserted in the DOM document later.
-         response.getRequestedSecurityToken().setAny(null);
+         if (response.getRequestedSecurityToken() != null)
+         {
+            tokenElements.add((Element) response.getRequestedSecurityToken().getAny());
+            // we don't want to marshall any token - it will be inserted in the DOM document later.
+            response.getRequestedSecurityToken().setAny(null);
+         }
       }
 
       Document result = null;
       try
       {
-         // marshall the response to a document and insert the issued token directly on the document.
+         // marshall the response to a document and insert the issued tokens directly into the document.
          result = DocumentUtil.createDocument();
          this.marshaller.marshal(this.objectFactory.createRequestSecurityTokenResponseCollection(collection
                .getDelegate()), result);
 
-         // the document is a ws-trust template - we need to insert the token in the appropriate element.
-         if (tokenElement != null)
+         // the document is a ws-trust template - we need to insert the tokens in the appropriate elements.
+         if (!tokenElements.isEmpty())
          {
-            Node node = this.findNodeByNameNS(result, "RequestedSecurityToken", WSTrustConstants.BASE_NAMESPACE);
-            node.appendChild(result.importNode(tokenElement, true));
+            NodeList nodes = result.getElementsByTagNameNS(WSTrustConstants.BASE_NAMESPACE, "RequestedSecurityToken");
+            for (int i = 0; i < nodes.getLength(); i++)
+            {
+               Node node = nodes.item(i);
+               node.appendChild(result.importNode(tokenElements.get(i), true));
+            }
          }
          if (trace)
          {
@@ -328,6 +409,7 @@
 
    /**
     * Return the {@code SAMLDocumentHolder} for the thread
+    * 
     * @return
     */
    public SAMLDocumentHolder getSAMLDocumentHolderOnThread()
@@ -340,12 +422,9 @@
     * Finds in the specified document a node that matches the specified name and namespace.
     * </p>
     * 
-    * @param document
-    *           the {@code Document} instance upon which the search is made.
-    * @param localName
-    *           a {@code String} containing the local name of the searched node.
-    * @param namespace
-    *           a {@code String} containing the namespace of the searched node.
+    * @param document the {@code Document} instance upon which the search is made.
+    * @param localName a {@code String} containing the local name of the searched node.
+    * @param namespace a {@code String} containing the namespace of the searched node.
     * @return a {@code Node} representing the searched node. If more than one node is found in the document, the first
     *         one will be returned. If no nodes were found according to the search parameters, then {@code null} is
     *         returned.
@@ -359,4 +438,20 @@
       return list.item(0);
    }
 
+   /**
+    * <p>
+    * Verifies if the specified {@code String} represents one of the valid WS-Trust batch request types or not.
+    * </p>
+    * 
+    * @param requestType the {@code String} to be verified.
+    * @return {@code true} if the request type matches one of the WS-Trust batch requests; {@code false} otherwise.
+    */
+   private boolean isValidBatchRequestType(String requestType)
+   {
+      // the request type must match one of the WS-Trust batch request types.
+      return (requestType.equals(WSTrustConstants.BATCH_ISSUE_REQUEST)
+            || requestType.equals(WSTrustConstants.BATCH_RENEW_REQUEST)
+            || requestType.equals(WSTrustConstants.BATCH_CANCEL_REQUEST) || requestType
+            .equals(WSTrustConstants.BATCH_VALIDATE_REQUEST));
+   }
 }
\ No newline at end of file

Modified: federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/WSTrustRequestHandler.java
===================================================================
--- federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/WSTrustRequestHandler.java	2010-08-25 21:17:40 UTC (rev 388)
+++ federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/WSTrustRequestHandler.java	2010-09-07 01:27:15 UTC (rev 389)
@@ -107,5 +107,5 @@
     * @return
     * @throws WSTrustException
     */
-   public Document postProcess(Document rstrDocument, RequestSecurityToken request) throws WSTrustException;
+   public Document postProcess(Document rstrDocument, RequestSecurityToken... requests) throws WSTrustException;
 }

Modified: federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/wrappers/RequestSecurityToken.java
===================================================================
--- federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/wrappers/RequestSecurityToken.java	2010-08-25 21:17:40 UTC (rev 388)
+++ federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/wrappers/RequestSecurityToken.java	2010-09-07 01:27:15 UTC (rev 389)
@@ -272,17 +272,6 @@
    }
 
    /**
-    * Creates an instance of {@code RequestSecurityTokenType} and {@code Document}
-    * @param delegate
-    * @param rstDocument
-    */
-   public RequestSecurityToken(RequestSecurityTokenType delegate, Document rstDocument)
-   {
-      this(delegate);
-      this.rstDocument = rstDocument;
-   }
-
-   /**
     * <p>
     * Obtains the {@code URI} that identifies the token type.
     * </p>

Modified: federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/wrappers/RequestSecurityTokenCollection.java
===================================================================
--- federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/wrappers/RequestSecurityTokenCollection.java	2010-08-25 21:17:40 UTC (rev 388)
+++ federation/branches/Branch_1_x/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/wrappers/RequestSecurityTokenCollection.java	2010-09-07 01:27:15 UTC (rev 389)
@@ -1,23 +1,19 @@
 /*
- * JBoss, Home of Professional Open Source.
- * Copyright 2009, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file in the
- * distribution for a full listing of individual contributors. 
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ * JBoss, Home of Professional Open Source. Copyright 2009, Red Hat Middleware LLC, and individual contributors as
+ * indicated by the @author tags. See the copyright.txt file in the distribution for a full listing of individual
+ * contributors.
+ * 
+ * This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any
+ * later version.
+ * 
+ * This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public License along with this software; if not, write to
+ * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA, or see the FSF site:
+ * http://www.fsf.org.
  */
 package org.picketlink.identity.federation.core.wstrust.wrappers;
 
@@ -25,8 +21,13 @@
 import java.util.Collections;
 import java.util.List;
 
+import org.picketlink.identity.federation.core.exceptions.ConfigurationException;
+import org.picketlink.identity.federation.core.saml.v2.util.DocumentUtil;
+import org.picketlink.identity.federation.core.wstrust.WSTrustConstants;
 import org.picketlink.identity.federation.ws.trust.RequestSecurityTokenCollectionType;
 import org.picketlink.identity.federation.ws.trust.RequestSecurityTokenType;
+import org.w3c.dom.Document;
+import org.w3c.dom.NodeList;
 
 /**
  * <p>
@@ -71,6 +72,40 @@
 
    /**
     * <p>
+    * Creates an instance of {@code RequestSecurityTokenCollection} using the specified delegate and document.
+    * </p>
+    * 
+    * @param delegate the JAXB {@code RequestSecurityTokenCollectionType} that represents a WS-Trust request collection.
+    * @param document the {@code Document} that contains the original WS-Trust batch request message.
+    */
+   public RequestSecurityTokenCollection(RequestSecurityTokenCollectionType delegate, Document document)
+   {
+      this.delegate = delegate;
+      this.requestSecurityTokens = new ArrayList<RequestSecurityToken>();
+      for (RequestSecurityTokenType request : delegate.getRequestSecurityToken())
+         this.requestSecurityTokens.add(new RequestSecurityToken(request));
+
+      // retrieve a list of the individual request nodes.
+      NodeList list = document.getElementsByTagNameNS(WSTrustConstants.BASE_NAMESPACE, "RequestSecurityToken");
+
+      // iterate through the nodes and set each node in its respective request object.
+      try
+      {
+         for (int i = 0; i < list.getLength(); i++)
+         {
+            Document doc = DocumentUtil.createDocument();
+            doc.appendChild(doc.importNode(list.item(i), true));
+            this.requestSecurityTokens.get(i).setRSTDocument(doc);
+         }
+      }
+      catch (ConfigurationException ce)
+      {
+         throw new RuntimeException("Error creating document: " + ce.getMessage(), ce);
+      }
+   }
+
+   /**
+    * <p>
     * Obtains the collection of {@code RequestSecurityToken} objects. The returned collection is immutable, so addition
     * or removal of requests must be carried by the appropriate add/remove methods.
     * </p>
@@ -94,7 +129,7 @@
       this.delegate.getRequestSecurityToken().add(request.getDelegate());
       this.requestSecurityTokens.add(request);
    }
- 
+
    /**
     * <p>
     * Removes the specified {@code RequestSecurityToken} object from the collection of token requests.
@@ -107,7 +142,7 @@
       this.delegate.getRequestSecurityToken().remove(request.getDelegate());
       this.requestSecurityTokens.remove(request);
    }
-   
+
    /**
     * <p>
     * Obtains a reference to the {@code RequestSecurityTokenCollectionType} delegate.

Modified: federation/branches/Branch_1_x/picketlink-fed-core/src/test/java/org/picketlink/test/identity/federation/core/saml/v2/X500AttributeUnitTestCase.java
===================================================================
--- federation/branches/Branch_1_x/picketlink-fed-core/src/test/java/org/picketlink/test/identity/federation/core/saml/v2/X500AttributeUnitTestCase.java	2010-08-25 21:17:40 UTC (rev 388)
+++ federation/branches/Branch_1_x/picketlink-fed-core/src/test/java/org/picketlink/test/identity/federation/core/saml/v2/X500AttributeUnitTestCase.java	2010-09-07 01:27:15 UTC (rev 389)
@@ -84,7 +84,7 @@
       //marshaller.marshal(jaxb, System.out);
       
       Document samlDom = DocumentUtil.getDocument(new String(baos.toByteArray()));
-      NodeList nl = samlDom.getElementsByTagName("Attribute");     
+      NodeList nl = samlDom.getElementsByTagNameNS("urn:oasis:names:tc:SAML:2.0:assertion", "Attribute");     
       assertEquals("nodes = 2", 2, nl.getLength());
       
       String x500NS = JBossSAMLURIConstants.X500_NSURI.get();

Modified: federation/branches/Branch_1_x/picketlink-fed-core/src/test/java/org/picketlink/test/identity/federation/core/wstrust/PicketLinkSTSUnitTestCase.java
===================================================================
--- federation/branches/Branch_1_x/picketlink-fed-core/src/test/java/org/picketlink/test/identity/federation/core/wstrust/PicketLinkSTSUnitTestCase.java	2010-08-25 21:17:40 UTC (rev 388)
+++ federation/branches/Branch_1_x/picketlink-fed-core/src/test/java/org/picketlink/test/identity/federation/core/wstrust/PicketLinkSTSUnitTestCase.java	2010-09-07 01:27:15 UTC (rev 389)
@@ -62,6 +62,7 @@
 import org.picketlink.identity.federation.core.wstrust.wrappers.BaseRequestSecurityTokenResponse;
 import org.picketlink.identity.federation.core.wstrust.wrappers.Lifetime;
 import org.picketlink.identity.federation.core.wstrust.wrappers.RequestSecurityToken;
+import org.picketlink.identity.federation.core.wstrust.wrappers.RequestSecurityTokenCollection;
 import org.picketlink.identity.federation.core.wstrust.wrappers.RequestSecurityTokenResponse;
 import org.picketlink.identity.federation.core.wstrust.wrappers.RequestSecurityTokenResponseCollection;
 import org.picketlink.identity.federation.saml.v2.assertion.AssertionType;
@@ -254,7 +255,11 @@
             .parseRequestSecurityTokenResponse(responseMessage);
 
       // validate the security token response.
-      this.validateCustomTokenResponse(baseResponse);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size());
+      this.validateCustomTokenResponse(collection.getRequestSecurityTokenResponses().get(0), "testcontext");
    }
 
    /**
@@ -310,7 +315,12 @@
             .parseRequestSecurityTokenResponse(responseMessage);
 
       // validate the security token response.
-      this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_BEARER_URI);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size());
+      this.validateSAMLAssertionResponse(collection.getRequestSecurityTokenResponses().get(0), "testcontext", "jduke",
+            SAMLUtil.SAML2_BEARER_URI);
    }
 
    /**
@@ -338,7 +348,11 @@
             .parseRequestSecurityTokenResponse(responseMessage);
 
       // validate the security token response.
-      this.validateCustomTokenResponse(baseResponse);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size());
+      this.validateCustomTokenResponse(collection.getRequestSecurityTokenResponses().get(0), "testcontext");
    }
 
    /**
@@ -365,8 +379,12 @@
             .parseRequestSecurityTokenResponse(responseMessage);
 
       // validate the security token response.
-      AssertionType assertion = this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke",
-            SAMLUtil.SAML2_BEARER_URI);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size());
+      AssertionType assertion = this.validateSAMLAssertionResponse(
+            collection.getRequestSecurityTokenResponses().get(0), "testcontext", "jduke", SAMLUtil.SAML2_BEARER_URI);
 
       // in this scenario, the conditions section should have an audience restriction.
       ConditionsType conditions = assertion.getConditions();
@@ -407,7 +425,12 @@
             .parseRequestSecurityTokenResponse(responseMessage);
 
       // validate the security token response (assertion principal should be anotherduke as specified by OnBehalfOf).
-      this.validateSAMLAssertionResponse(baseResponse, "testcontext", "anotherduke", SAMLUtil.SAML2_SENDER_VOUCHES_URI);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size());
+      this.validateSAMLAssertionResponse(collection.getRequestSecurityTokenResponses().get(0), "testcontext",
+            "anotherduke", SAMLUtil.SAML2_SENDER_VOUCHES_URI);
    }
 
    /**
@@ -439,7 +462,12 @@
             .parseRequestSecurityTokenResponse(responseMessage);
 
       // validate the security token response.
-      AssertionType assertion = this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke",
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size());
+      AssertionType assertion = this.validateSAMLAssertionResponse(
+            collection.getRequestSecurityTokenResponses().get(0), "testcontext", "jduke",
             SAMLUtil.SAML2_HOLDER_OF_KEY_URI);
       // validate the holder of key contents.
       SubjectConfirmationType subjConfirmation = (SubjectConfirmationType) assertion.getSubject().getContent().get(1)
@@ -447,7 +475,7 @@
       this.validateHolderOfKeyContents(subjConfirmation, WSTrustConstants.KEY_TYPE_SYMMETRIC, null, false);
 
       // check if the response contains the STS-generated key.
-      RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse;
+      collection = (RequestSecurityTokenResponseCollection) baseResponse;
       RequestSecurityTokenResponse response = collection.getRequestSecurityTokenResponses().get(0);
       RequestedProofTokenType proofToken = response.getRequestedProofToken();
       assertNotNull("Unexpected null proof token", proofToken);
@@ -498,14 +526,19 @@
             .parseRequestSecurityTokenResponse(responseMessage);
 
       // validate the security token response.
-      AssertionType assertion = this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke",
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size());
+      AssertionType assertion = this.validateSAMLAssertionResponse(
+            collection.getRequestSecurityTokenResponses().get(0), "testcontext", "jduke",
             SAMLUtil.SAML2_HOLDER_OF_KEY_URI);
       // validate the holder of key contents.
       SubjectConfirmationType subjConfirmation = (SubjectConfirmationType) assertion.getSubject().getContent().get(1)
             .getValue();
       this.validateHolderOfKeyContents(subjConfirmation, WSTrustConstants.KEY_TYPE_SYMMETRIC, null, false);
 
-      RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse;
+      collection = (RequestSecurityTokenResponseCollection) baseResponse;
       RequestSecurityTokenResponse response = collection.getRequestSecurityTokenResponses().get(0);
       RequestedProofTokenType proofToken = response.getRequestedProofToken();
       assertNotNull("Unexpected null proof token", proofToken);
@@ -558,7 +591,12 @@
             .parseRequestSecurityTokenResponse(responseMessage);
 
       // validate the security token response.
-      AssertionType assertion = this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke",
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size());
+      AssertionType assertion = this.validateSAMLAssertionResponse(
+            collection.getRequestSecurityTokenResponses().get(0), "testcontext", "jduke",
             SAMLUtil.SAML2_HOLDER_OF_KEY_URI);
       // validate the holder of key contents.
       SubjectConfirmationType subjConfirmation = (SubjectConfirmationType) assertion.getSubject().getContent().get(1)
@@ -595,7 +633,12 @@
             .parseRequestSecurityTokenResponse(responseMessage);
 
       // validate the security token response.
-      AssertionType assertion = this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke",
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size());
+      AssertionType assertion = this.validateSAMLAssertionResponse(
+            collection.getRequestSecurityTokenResponses().get(0), "testcontext", "jduke",
             SAMLUtil.SAML2_HOLDER_OF_KEY_URI);
       // validate the holder of key contents.
       SubjectConfirmationType subjConfirmation = (SubjectConfirmationType) assertion.getSubject().getContent().get(1)
@@ -617,7 +660,7 @@
       RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST,
             SAMLUtil.SAML2_TOKEN_TYPE, null);
 
-      // use the factory to marshall the request.
+      // use the factory to marshal the request.
       WSTrustJAXBFactory factory = WSTrustJAXBFactory.getInstance();
       Source requestMessage = factory.marshallRequestSecurityToken(request);
 
@@ -626,8 +669,13 @@
       BaseRequestSecurityTokenResponse baseResponse = factory.parseRequestSecurityTokenResponse(responseMessage);
 
       // validate the response and get the SAML assertion from the request.
-      this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_BEARER_URI);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
       RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size());
+      this.validateSAMLAssertionResponse(collection.getRequestSecurityTokenResponses().get(0), "testcontext", "jduke",
+            SAMLUtil.SAML2_BEARER_URI);
+
       Element assertion = (Element) collection.getRequestSecurityTokenResponses().get(0).getRequestedSecurityToken()
             .getAny();
 
@@ -685,7 +733,7 @@
       RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST, null,
             "http://services.testcorp.org/provider2");
 
-      // use the factory to marshall the request.
+      // use the factory to marshal the request.
       WSTrustJAXBFactory factory = WSTrustJAXBFactory.getInstance();
       Source requestMessage = factory.marshallRequestSecurityToken(request);
 
@@ -694,8 +742,12 @@
       BaseRequestSecurityTokenResponse baseResponse = factory.parseRequestSecurityTokenResponse(responseMessage);
 
       // validate the response and get the SAML assertion from the request.
-      this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_BEARER_URI);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
       RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size());
+      this.validateSAMLAssertionResponse(collection.getRequestSecurityTokenResponses().get(0), "testcontext", "jduke",
+            SAMLUtil.SAML2_BEARER_URI);
       Element assertionElement = (Element) collection.getRequestSecurityTokenResponses().get(0)
             .getRequestedSecurityToken().getAny();
 
@@ -710,8 +762,13 @@
       baseResponse = factory.parseRequestSecurityTokenResponse(responseMessage);
 
       // validate the renew response contents and get the renewed token.
-      this.validateSAMLAssertionResponse(baseResponse, "renewcontext", "jduke", SAMLUtil.SAML2_BEARER_URI);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
       collection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size());
+      this.validateSAMLAssertionResponse(collection.getRequestSecurityTokenResponses().get(0), "renewcontext", "jduke",
+            SAMLUtil.SAML2_BEARER_URI);
+      collection = (RequestSecurityTokenResponseCollection) baseResponse;
       Element renewedAssertionElement = (Element) collection.getRequestSecurityTokenResponses().get(0)
             .getRequestedSecurityToken().getAny();
 
@@ -742,7 +799,7 @@
       RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST,
             SAMLUtil.SAML2_TOKEN_TYPE, null);
 
-      // use the factory to marshall the request.
+      // use the factory to marshal the request.
       WSTrustJAXBFactory factory = WSTrustJAXBFactory.getInstance();
       Source requestMessage = factory.marshallRequestSecurityToken(request);
 
@@ -751,8 +808,12 @@
       BaseRequestSecurityTokenResponse baseResponse = factory.parseRequestSecurityTokenResponse(responseMessage);
 
       // validate the response and get the SAML assertion from the request.
-      this.validateSAMLAssertionResponse(baseResponse, "testcontext", "jduke", SAMLUtil.SAML2_BEARER_URI);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
       RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size());
+      this.validateSAMLAssertionResponse(collection.getRequestSecurityTokenResponses().get(0), "testcontext", "jduke",
+            SAMLUtil.SAML2_BEARER_URI);
       Element assertion = (Element) collection.getRequestSecurityTokenResponses().get(0).getRequestedSecurityToken()
             .getAny();
 
@@ -817,6 +878,413 @@
 
    /**
     * <p>
+    * This test sends a {@code BatchIssue} request to the STS in an attempt to generate two different security tokens
+    * using a single WS-Trust request message.
+    * </p>
+    * 
+    * @throws Exception if an error occurs while running the test.
+    */
+   public void testBatchIssue() throws Exception
+   {
+      // create two issue requests - in this case the user wants two different tokens from the STS.
+      RequestSecurityToken request1 = this.createRequest("context1", WSTrustConstants.BATCH_ISSUE_REQUEST,
+            SAMLUtil.SAML2_TOKEN_TYPE, null);
+      RequestSecurityToken request2 = this.createRequest("context2", WSTrustConstants.BATCH_ISSUE_REQUEST,
+            "http://www.tokens.org/SpecialToken", null);
+
+      // create a request collection that includes the two issue requests.
+      RequestSecurityTokenCollection collection = new RequestSecurityTokenCollection();
+      collection.addRequestSecurityToken(request1);
+      collection.addRequestSecurityToken(request2);
+
+      // use the factory to marshal the request collection.
+      WSTrustJAXBFactory factory = WSTrustJAXBFactory.getInstance();
+      Source requestMessage = factory.marshallRequestSecurityTokenCollection(collection);
+
+      // invoke the token service.
+      Source responseMessage = this.tokenService.invoke(requestMessage);
+
+      // validate the overall response.
+      BaseRequestSecurityTokenResponse baseResponse = factory.parseRequestSecurityTokenResponse(responseMessage);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      RequestSecurityTokenResponseCollection responseCollection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 2, responseCollection.getRequestSecurityTokenResponses().size());
+
+      // first response should contain a SAML assertion.
+      this.validateSAMLAssertionResponse(responseCollection.getRequestSecurityTokenResponses().get(0), "context1",
+            "jduke", SAMLUtil.SAML2_BEARER_URI);
+
+      // second response should contain a custom token.
+      this.validateCustomTokenResponse(responseCollection.getRequestSecurityTokenResponses().get(1), "context2");
+   }
+
+   /**
+    * <p>
+    * This test sends a {@code BatchValidate} request to the STS in an attempt to validate two different security tokens
+    * using a single WS-Trust request message.
+    * </p>
+    * 
+    * @throws Exception if an error occurs while running the test.
+    */
+   public void testBatchValidate() throws Exception
+   {
+      // first issue two assertions, one using the token type and the other using applies to.
+      RequestSecurityToken request1 = this.createRequest("context1", WSTrustConstants.BATCH_ISSUE_REQUEST,
+            SAMLUtil.SAML2_TOKEN_TYPE, null);
+      RequestSecurityToken request2 = this.createRequest("context2", WSTrustConstants.BATCH_ISSUE_REQUEST, null,
+            "http://services.testcorp.org/provider2");
+
+      // create a request collection that includes the two issue requests.
+      RequestSecurityTokenCollection collection = new RequestSecurityTokenCollection();
+      collection.addRequestSecurityToken(request1);
+      collection.addRequestSecurityToken(request2);
+
+      // use the factory to marshal the request collection.
+      WSTrustJAXBFactory factory = WSTrustJAXBFactory.getInstance();
+      Source requestMessage = factory.marshallRequestSecurityTokenCollection(collection);
+
+      // invoke the token service.
+      Source responseMessage = this.tokenService.invoke(requestMessage);
+
+      // validate the response and get the SAML assertions from the request.
+      BaseRequestSecurityTokenResponse baseResponse = factory.parseRequestSecurityTokenResponse(responseMessage);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      RequestSecurityTokenResponseCollection responseCollection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 2, responseCollection.getRequestSecurityTokenResponses().size());
+
+      Element assertion1 = (Element) responseCollection.getRequestSecurityTokenResponses().get(0)
+            .getRequestedSecurityToken().getAny();
+      Element assertion2 = (Element) responseCollection.getRequestSecurityTokenResponses().get(1)
+            .getRequestedSecurityToken().getAny();
+
+      // now construct a WS-Trust batch validate request with the generated assertion.
+      request1 = this.createRequest("validatecontext1", WSTrustConstants.BATCH_VALIDATE_REQUEST,
+            WSTrustConstants.STATUS_TYPE, null);
+      ValidateTargetType validateTarget1 = new ValidateTargetType();
+      validateTarget1.setAny(assertion1);
+      request1.setValidateTarget(validateTarget1);
+
+      request2 = this.createRequest("validatecontext2", WSTrustConstants.BATCH_VALIDATE_REQUEST,
+            WSTrustConstants.STATUS_TYPE, null);
+      ValidateTargetType validateTarget2 = new ValidateTargetType();
+      validateTarget2.setAny(assertion2);
+      request2.setValidateTarget(validateTarget2);
+
+      collection = new RequestSecurityTokenCollection();
+      collection.addRequestSecurityToken(request1);
+      collection.addRequestSecurityToken(request2);
+
+      // invoke the token service.
+      responseMessage = this.tokenService.invoke(factory.marshallRequestSecurityTokenCollection(collection));
+
+      // check the validation response.
+      baseResponse = factory.parseRequestSecurityTokenResponse(responseMessage);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      responseCollection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 2, responseCollection.getRequestSecurityTokenResponses().size());
+
+      RequestSecurityTokenResponse response = responseCollection.getRequestSecurityTokenResponses().get(0);
+      assertEquals("Unexpected response context", "validatecontext1", response.getContext());
+      assertEquals("Unexpected token type", WSTrustConstants.STATUS_TYPE, response.getTokenType().toString());
+      StatusType status = response.getStatus();
+      assertNotNull("Unexpected null status", status);
+      assertEquals("Unexpected status code", WSTrustConstants.STATUS_CODE_VALID, status.getCode());
+      assertEquals("Unexpected status reason", "SAMLV2.0 Assertion successfuly validated", status.getReason());
+
+      response = responseCollection.getRequestSecurityTokenResponses().get(1);
+      assertEquals("Unexpected response context", "validatecontext2", response.getContext());
+      assertEquals("Unexpected token type", WSTrustConstants.STATUS_TYPE, response.getTokenType().toString());
+      status = response.getStatus();
+      assertNotNull("Unexpected null status", status);
+      assertEquals("Unexpected status code", WSTrustConstants.STATUS_CODE_VALID, status.getCode());
+      assertEquals("Unexpected status reason", "SAMLV2.0 Assertion successfuly validated", status.getReason());
+
+      // now let's temper the first SAML assertion and try to validate them all again.
+      assertion1.getFirstChild().getFirstChild().setNodeValue("Tempered Issuer");
+      request1.getValidateTarget().setAny(assertion1);
+      request2.getValidateTarget().setAny(assertion2);
+      responseMessage = this.tokenService.invoke(factory.marshallRequestSecurityTokenCollection(collection));
+
+      baseResponse = factory.parseRequestSecurityTokenResponse(responseMessage);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      responseCollection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 2, responseCollection.getRequestSecurityTokenResponses().size());
+
+      // this time the first assertion should fail the digital signature verification.
+      response = responseCollection.getRequestSecurityTokenResponses().get(0);
+      assertEquals("Unexpected response context", "validatecontext1", response.getContext());
+      assertEquals("Unexpected token type", WSTrustConstants.STATUS_TYPE, response.getTokenType().toString());
+      status = response.getStatus();
+      assertNotNull("Unexpected null status", status);
+      assertEquals("Unexpected status code", WSTrustConstants.STATUS_CODE_INVALID, status.getCode());
+      assertEquals("Unexpected status reason", "Validation failure: digital signature is invalid", status.getReason());
+
+      response = responseCollection.getRequestSecurityTokenResponses().get(1);
+      assertEquals("Unexpected response context", "validatecontext2", response.getContext());
+      assertEquals("Unexpected token type", WSTrustConstants.STATUS_TYPE, response.getTokenType().toString());
+      status = response.getStatus();
+      assertNotNull("Unexpected null status", status);
+      assertEquals("Unexpected status code", WSTrustConstants.STATUS_CODE_VALID, status.getCode());
+      assertEquals("Unexpected status reason", "SAMLV2.0 Assertion successfuly validated", status.getReason());
+   }
+
+   /**
+    * <p>
+    * This test sends a {@code BatchRenew} request to the STS in an attempt to renew two different security tokens using
+    * a single WS-Trust request message.
+    * </p>
+    * 
+    * @throws Exception if an error occurs while running the test.
+    */
+   public void testBatchRenew() throws Exception
+   {
+      // first issue two assertions, one using the token type and the other using applies to.
+      RequestSecurityToken request1 = this.createRequest("context1", WSTrustConstants.BATCH_ISSUE_REQUEST,
+            SAMLUtil.SAML2_TOKEN_TYPE, null);
+      RequestSecurityToken request2 = this.createRequest("context2", WSTrustConstants.BATCH_ISSUE_REQUEST, null,
+            "http://services.testcorp.org/provider2");
+
+      // create a request collection that includes the two issue requests.
+      RequestSecurityTokenCollection collection = new RequestSecurityTokenCollection();
+      collection.addRequestSecurityToken(request1);
+      collection.addRequestSecurityToken(request2);
+
+      // use the factory to marshal the request collection.
+      WSTrustJAXBFactory factory = WSTrustJAXBFactory.getInstance();
+      Source requestMessage = factory.marshallRequestSecurityTokenCollection(collection);
+
+      // invoke the token service.
+      Source responseMessage = this.tokenService.invoke(requestMessage);
+
+      // validate the response and get the SAML assertions from the request.
+      BaseRequestSecurityTokenResponse baseResponse = factory.parseRequestSecurityTokenResponse(responseMessage);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      RequestSecurityTokenResponseCollection responseCollection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 2, responseCollection.getRequestSecurityTokenResponses().size());
+
+      Element assertion1 = (Element) responseCollection.getRequestSecurityTokenResponses().get(0)
+            .getRequestedSecurityToken().getAny();
+      Element assertion2 = (Element) responseCollection.getRequestSecurityTokenResponses().get(1)
+            .getRequestedSecurityToken().getAny();
+
+      // now construct a WS-Trust batch validate request with the generated assertion.
+      request1 = this.createRequest("renewcontext1", WSTrustConstants.BATCH_RENEW_REQUEST,
+            WSTrustConstants.STATUS_TYPE, null);
+      RenewTargetType renewTarget1 = new RenewTargetType();
+      renewTarget1.setAny(assertion1);
+      request1.setRenewTarget(renewTarget1);
+
+      request2 = this.createRequest("renewcontext2", WSTrustConstants.BATCH_RENEW_REQUEST,
+            WSTrustConstants.STATUS_TYPE, null);
+      RenewTargetType renewTarget2 = new RenewTargetType();
+      renewTarget2.setAny(assertion2);
+      request2.setRenewTarget(renewTarget2);
+
+      collection = new RequestSecurityTokenCollection();
+      collection.addRequestSecurityToken(request1);
+      collection.addRequestSecurityToken(request2);
+
+      // invoke the token service.
+      responseMessage = this.tokenService.invoke(factory.marshallRequestSecurityTokenCollection(collection));
+      baseResponse = factory.parseRequestSecurityTokenResponse(responseMessage);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      responseCollection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 2, responseCollection.getRequestSecurityTokenResponses().size());
+
+      // extract the renewed assertions.
+      Element renewedAssertionElement1 = (Element) responseCollection.getRequestSecurityTokenResponses().get(0)
+            .getRequestedSecurityToken().getAny();
+      Element renewedAssertionElement2 = (Element) responseCollection.getRequestSecurityTokenResponses().get(1)
+            .getRequestedSecurityToken().getAny();
+
+      // check if both assertions have been renewed: lifetime must have been updated and ID must have been renewed.
+      AssertionType originalAssertion1 = SAMLUtil.fromElement(assertion1);
+      AssertionType originalAssertion2 = SAMLUtil.fromElement(assertion2);
+      AssertionType renewedAssertion1 = SAMLUtil.fromElement(renewedAssertionElement1);
+      AssertionType renewedAssertion2 = SAMLUtil.fromElement(renewedAssertionElement2);
+
+      // assertions should have different ids and lifetimes.
+      assertFalse("Renewed assertion should have a unique id", originalAssertion1.getID().equals(
+            renewedAssertion1.getID()));
+      assertFalse("Renewed assertion should have a unique id", originalAssertion2.getID().equals(
+            renewedAssertion2.getID()));
+      assertEquals(DatatypeConstants.LESSER, originalAssertion1.getConditions().getNotBefore().compare(
+            renewedAssertion1.getConditions().getNotBefore()));
+      assertEquals(DatatypeConstants.LESSER, originalAssertion1.getConditions().getNotOnOrAfter().compare(
+            renewedAssertion1.getConditions().getNotOnOrAfter()));
+      assertEquals(DatatypeConstants.LESSER, originalAssertion2.getConditions().getNotBefore().compare(
+            renewedAssertion2.getConditions().getNotBefore()));
+      assertEquals(DatatypeConstants.LESSER, originalAssertion2.getConditions().getNotOnOrAfter().compare(
+            renewedAssertion2.getConditions().getNotOnOrAfter()));
+   }
+
+   /**
+    * <p>
+    * This test sends a {@code BatchCancel} request to the STS in an attempt to cancel three different security tokens
+    * using a single WS-Trust request message.
+    * </p>
+    * 
+    * @throws Exception if an error occurs while running the test.
+    */
+   public void testBatchCancel() throws Exception
+   {
+      // first issue three assertions, two using the token type and one using applies to.
+      RequestSecurityToken request1 = this.createRequest("context1", WSTrustConstants.BATCH_ISSUE_REQUEST,
+            SAMLUtil.SAML2_TOKEN_TYPE, null);
+      RequestSecurityToken request2 = this.createRequest("context2", WSTrustConstants.BATCH_ISSUE_REQUEST,
+            SAMLUtil.SAML2_TOKEN_TYPE, null);
+      RequestSecurityToken request3 = this.createRequest("context3", WSTrustConstants.BATCH_ISSUE_REQUEST, null,
+            "http://services.testcorp.org/provider2");
+
+      // create a request collection that includes the three issue requests.
+      RequestSecurityTokenCollection collection = new RequestSecurityTokenCollection();
+      collection.addRequestSecurityToken(request1);
+      collection.addRequestSecurityToken(request2);
+      collection.addRequestSecurityToken(request3);
+
+      // use the factory to marshal the request collection.
+      WSTrustJAXBFactory factory = WSTrustJAXBFactory.getInstance();
+      Source requestMessage = factory.marshallRequestSecurityTokenCollection(collection);
+
+      // invoke the token service.
+      Source responseMessage = this.tokenService.invoke(requestMessage);
+
+      // validate the response and get the SAML assertions from the request.
+      BaseRequestSecurityTokenResponse baseResponse = factory.parseRequestSecurityTokenResponse(responseMessage);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      RequestSecurityTokenResponseCollection responseCollection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 3, responseCollection.getRequestSecurityTokenResponses().size());
+
+      Element assertion1 = (Element) responseCollection.getRequestSecurityTokenResponses().get(0)
+            .getRequestedSecurityToken().getAny();
+      Element assertion2 = (Element) responseCollection.getRequestSecurityTokenResponses().get(1)
+            .getRequestedSecurityToken().getAny();
+      Element assertion3 = (Element) responseCollection.getRequestSecurityTokenResponses().get(2)
+            .getRequestedSecurityToken().getAny();
+
+      // now cancel two of the three assertions using the BatchCancel request type.
+      request1 = this.createRequest("cancelcontext1", WSTrustConstants.BATCH_CANCEL_REQUEST,
+            WSTrustConstants.STATUS_TYPE, null);
+      CancelTargetType cancelTarget1 = new CancelTargetType();
+      cancelTarget1.setAny(assertion1);
+      request1.setCancelTarget(cancelTarget1);
+
+      request2 = this.createRequest("cancelcontext2", WSTrustConstants.BATCH_CANCEL_REQUEST,
+            WSTrustConstants.STATUS_TYPE, null);
+      CancelTargetType cancelTarget2 = new CancelTargetType();
+      cancelTarget2.setAny(assertion2);
+      request2.setCancelTarget(cancelTarget2);
+
+      collection = new RequestSecurityTokenCollection();
+      collection.addRequestSecurityToken(request1);
+      collection.addRequestSecurityToken(request2);
+
+      // invoke the token service.
+      responseMessage = this.tokenService.invoke(factory.marshallRequestSecurityTokenCollection(collection));
+      baseResponse = factory.parseRequestSecurityTokenResponse(responseMessage);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      responseCollection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 2, responseCollection.getRequestSecurityTokenResponses().size());
+
+      // validate the response message.
+      RequestSecurityTokenResponse response = responseCollection.getRequestSecurityTokenResponses().get(0);
+      assertEquals("Unexpected response context", "cancelcontext1", response.getContext());
+      assertNotNull("Cancel response should contain a RequestedTokenCancelled element", response
+            .getRequestedTokenCancelled());
+      response = responseCollection.getRequestSecurityTokenResponses().get(1);
+      assertEquals("Unexpected response context", "cancelcontext2", response.getContext());
+      assertNotNull("Cancel response should contain a RequestedTokenCancelled element", response
+            .getRequestedTokenCancelled());
+
+      // now let's validate all assertions using a batch validate request.
+      request1 = this.createRequest("validatecontext1", WSTrustConstants.BATCH_VALIDATE_REQUEST,
+            WSTrustConstants.STATUS_TYPE, null);
+      ValidateTargetType validateTarget1 = new ValidateTargetType();
+      validateTarget1.setAny(assertion1);
+      request1.setValidateTarget(validateTarget1);
+      request2 = this.createRequest("validatecontext2", WSTrustConstants.BATCH_VALIDATE_REQUEST,
+            WSTrustConstants.STATUS_TYPE, null);
+      ValidateTargetType validateTarget2 = new ValidateTargetType();
+      validateTarget2.setAny(assertion2);
+      request2.setValidateTarget(validateTarget2);
+      request3 = this.createRequest("validatecontext3", WSTrustConstants.BATCH_VALIDATE_REQUEST,
+            WSTrustConstants.STATUS_TYPE, null);
+      ValidateTargetType validateTarget3 = new ValidateTargetType();
+      validateTarget3.setAny(assertion3);
+      request3.setValidateTarget(validateTarget3);
+      
+      collection = new RequestSecurityTokenCollection();
+      collection.addRequestSecurityToken(request1);
+      collection.addRequestSecurityToken(request2);
+      collection.addRequestSecurityToken(request3);
+
+      // invoke the token service.
+      responseMessage = this.tokenService.invoke(factory.marshallRequestSecurityTokenCollection(collection));
+      baseResponse = factory.parseRequestSecurityTokenResponse(responseMessage);
+      assertNotNull("Unexpected null response", baseResponse);
+      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
+      responseCollection = (RequestSecurityTokenResponseCollection) baseResponse;
+      assertEquals("Unexpected number of responses", 3, responseCollection.getRequestSecurityTokenResponses().size());
+
+      // the first two assertions should have been considered invalid as they have been previously canceled.
+      response = responseCollection.getRequestSecurityTokenResponses().get(0);
+      assertEquals("Unexpected response context", "validatecontext1", response.getContext());
+      assertEquals("Unexpected token type", WSTrustConstants.STATUS_TYPE, response.getTokenType().toString());
+      StatusType status = response.getStatus();
+      assertNotNull("Unexpected null status", status);
+      assertEquals("Unexpected status code", WSTrustConstants.STATUS_CODE_INVALID, status.getCode());
+      assertEquals("Unexpected status reason", "Validation failure: assertion with id " + assertion1.getAttribute("ID")
+            + " has been canceled", status.getReason());
+
+      response = responseCollection.getRequestSecurityTokenResponses().get(1);
+      assertEquals("Unexpected response context", "validatecontext2", response.getContext());
+      assertEquals("Unexpected token type", WSTrustConstants.STATUS_TYPE, response.getTokenType().toString());
+      status = response.getStatus();
+      assertNotNull("Unexpected null status", status);
+      assertEquals("Unexpected status code", WSTrustConstants.STATUS_CODE_INVALID, status.getCode());
+      assertEquals("Unexpected status reason", "Validation failure: assertion with id " + assertion2.getAttribute("ID")
+            + " has been canceled", status.getReason());
+
+      // third assertion should be considered valid.
+      response = responseCollection.getRequestSecurityTokenResponses().get(2);
+      assertEquals("Unexpected response context", "validatecontext3", response.getContext());
+      assertEquals("Unexpected token type", WSTrustConstants.STATUS_TYPE, response.getTokenType().toString());
+      status = response.getStatus();
+      assertNotNull("Unexpected null status", status);
+      assertEquals("Unexpected status code", WSTrustConstants.STATUS_CODE_VALID, status.getCode());
+      assertEquals("Unexpected status reason", "SAMLV2.0 Assertion successfuly validated", status.getReason());
+      
+      // now try to renew one of the canceled assertions.
+      request1 = this.createRequest("renewcontext", WSTrustConstants.RENEW_REQUEST, null, null);
+      RenewTargetType renewTarget = new RenewTargetType();
+      renewTarget.setAny(assertion1);
+      request1.setRenewTarget(renewTarget);
+
+      // we should receive an exception when renewing the token.
+      try
+      {
+         this.tokenService.invoke(factory.marshallRequestSecurityToken(request1));
+         fail("Renewing a canceled token should result in an exception being thrown");
+      }
+      catch (WebServiceException we)
+      {
+         assertTrue("Unexpected cause type", we.getCause() instanceof WSTrustException);
+         assertEquals("Unexpected exception message", "Assertion with id " + assertion1.getAttribute("ID")
+               + " has been canceled and cannot be renewed", we.getCause().getMessage());
+      }
+
+   }
+
+   /**
+    * <p>
     * This test tries to request a token of an unknown type, checking if an exception is correctly thrown by the
     * security token service.
     * </p>
@@ -829,7 +1297,7 @@
       RequestSecurityToken request = this.createRequest("testcontext", WSTrustConstants.ISSUE_REQUEST,
             "http://www.tokens.org/UnknownToken", null);
 
-      // use the factory to marshall the request.
+      // use the factory to marshal the request.
       WSTrustJAXBFactory factory = WSTrustJAXBFactory.getInstance();
       Source requestMessage = factory.marshallRequestSecurityToken(request);
 
@@ -1080,20 +1548,15 @@
     * SpecialTokenProvider}.
     * </p>
     * 
-    * @param baseResponse a reference to the WS-Trust response that was sent by the STS.
+    * @param response a reference to the WS-Trust response that was sent by the STS.
+    * @param context a {@code String} representing the response context name.
     * @throws Exception if one of the validation performed fail.
     */
-   private void validateCustomTokenResponse(BaseRequestSecurityTokenResponse baseResponse) throws Exception
+   private void validateCustomTokenResponse(RequestSecurityTokenResponse response, String context) throws Exception
    {
 
       // =============================== WS-Trust Security Token Response Validation ===============================//
-
-      assertNotNull("Unexpected null response", baseResponse);
-      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
-      RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse;
-      assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size());
-      RequestSecurityTokenResponse response = collection.getRequestSecurityTokenResponses().get(0);
-      assertEquals("Unexpected response context", "testcontext", response.getContext());
+      assertEquals("Unexpected response context", context, response.getContext());
       assertEquals("Unexpected token type", "http://www.tokens.org/SpecialToken", response.getTokenType().toString());
       Lifetime lifetime = response.getLifetime();
       assertNotNull("Unexpected null token lifetime", lifetime);
@@ -1119,7 +1582,7 @@
     * SAML20TokenProvider}.
     * </p>
     * 
-    * @param baseResponse a reference to the WS-Trust response that was sent by the STS.
+    * @param response a reference to the WS-Trust response that was sent by the STS.
     * @param context the expected name of the response context.
     * @param principal the principal that is expected to be seen in the assertion subject.
     * @param confirmationMethod the confirmation method that is expected to be seen in the assertion subject.
@@ -1127,17 +1590,11 @@
     *         methods to perform extra validations depending on the scenario being tested.
     * @throws Exception if an error occurs while performing the validation.
     */
-   private AssertionType validateSAMLAssertionResponse(BaseRequestSecurityTokenResponse baseResponse, String context,
+   private AssertionType validateSAMLAssertionResponse(RequestSecurityTokenResponse response, String context,
          String principal, String confirmationMethod) throws Exception
    {
 
       // =============================== WS-Trust Security Token Response Validation ===============================//
-
-      assertNotNull("Unexpected null response", baseResponse);
-      assertTrue("Unexpected response type", baseResponse instanceof RequestSecurityTokenResponseCollection);
-      RequestSecurityTokenResponseCollection collection = (RequestSecurityTokenResponseCollection) baseResponse;
-      assertEquals("Unexpected number of responses", 1, collection.getRequestSecurityTokenResponses().size());
-      RequestSecurityTokenResponse response = collection.getRequestSecurityTokenResponses().get(0);
       assertEquals("Unexpected response context", context, response.getContext());
       assertEquals("Unexpected token type", SAMLUtil.SAML2_TOKEN_TYPE, response.getTokenType().toString());
       Lifetime lifetime = response.getLifetime();



More information about the picketlink-commits mailing list