[jboss-cvs] Picketlink SVN: r275 - in federation/trunk: picketlink-fed-core and 4 other directories.

jboss-cvs-commits at lists.jboss.org jboss-cvs-commits at lists.jboss.org
Fri Jun 4 09:46:13 EDT 2010


Author: sguilhen at redhat.com
Date: 2010-06-04 09:46:11 -0400 (Fri, 04 Jun 2010)
New Revision: 275

Added:
   federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/DefaultRevocationRegistry.java
   federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/FileBasedRevocationRegistry.java
   federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/JPABasedRevocationRegistry.java
   federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/RevocationRegistry.java
   federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/RevokedToken.java
   federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/saml/SecurityActions.java
Modified:
   federation/trunk/picketlink-fed-api/src/main/java/org/picketlink/identity/federation/api/wstrust/WSTrustClient.java
   federation/trunk/picketlink-fed-core/pom.xml
   federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/STSClient.java
   federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/saml/SAML20TokenProvider.java
   federation/trunk/picketlink-fed-core/src/test/java/org/picketlink/test/identity/federation/core/wstrust/PicketLinkSTSUnitTestCase.java
Log:
PLFED-78: Introduced the concept of a revocation registry. Two implementations have been provided: a file-based which stores canceled ids in files, and a JPA-based which stores the ids in a database.

Modified: federation/trunk/picketlink-fed-api/src/main/java/org/picketlink/identity/federation/api/wstrust/WSTrustClient.java
===================================================================
--- federation/trunk/picketlink-fed-api/src/main/java/org/picketlink/identity/federation/api/wstrust/WSTrustClient.java	2010-06-04 13:29:50 UTC (rev 274)
+++ federation/trunk/picketlink-fed-api/src/main/java/org/picketlink/identity/federation/api/wstrust/WSTrustClient.java	2010-06-04 13:46:11 UTC (rev 275)
@@ -164,6 +164,20 @@
 
    /**
     * <p>
+    * This method sends a WS-Trust cancel message to the STS in order to cancel (revoke) the specified security token.
+    * </p>
+    * 
+    * @param token the security token to be canceled.
+    * @return {@code true} if the token was successfully canceled; {@code false} otherwise.
+    * @throws WSTrustException if an error occurs while canceling the security token.
+    */
+   public boolean cancelToken(Element token) throws WSTrustException
+   {
+      return this.cancelInternal(token, 0);
+   }
+   
+   /**
+    * <p>
     * This method issues a token of the specified type for the specified service endpoint and has failover support when
     * more than one endpoint URI has been provided in the constructor. If a {@code ConnectException} occurs when sending
     * the WS-Trust request to one endpoint, the code makes a new attempt using the next URI until the request reaches an
@@ -261,6 +275,37 @@
 
    /**
     * <p>
+    * This method cancels the specified token and has failover support when more than one endpoint URI has been provided
+    * in the constructor. If a {@code ConnectException} occurs when sending the WS-Trust request to one endpoint, the
+    * code makes a new attempt using the next URI until the request reaches an STS instance or all URIs have been tried.
+    * </p>
+    * 
+    * @param token an {@code Element} representing the security token being canceled.
+    * @param clientIndex an {@code int} that indicates which of the {@code STSClient} instances should be used to perform
+    * the request.
+    * @return {@code true} if the token was canceled; {@code false} otherwise.
+    * @throws WSTrustException if a WS-Trust exception is thrown by the STS.
+    */
+   private boolean cancelInternal(Element token, int clientIndex) throws WSTrustException
+   {
+      STSClient client = this.clients[clientIndex];
+      try
+      {
+         return client.cancelToken(token);
+      }
+      catch (RuntimeException e)
+      {
+         // if this was a connection refused exception and we still have clients to try, call the next client.
+         if (this.isCausedByConnectException(e) && clientIndex < this.clients.length - 1)
+         {
+            return this.cancelInternal(token, ++clientIndex);
+         }
+         throw e;
+      }
+   }
+
+   /**
+    * <p>
     * Checks if the root of the specified {@code Throwable} is an instance of {@code java.net.ConnectException}. 
     * </p>
     * 

Modified: federation/trunk/picketlink-fed-core/pom.xml
===================================================================
--- federation/trunk/picketlink-fed-core/pom.xml	2010-06-04 13:29:50 UTC (rev 274)
+++ federation/trunk/picketlink-fed-core/pom.xml	2010-06-04 13:46:11 UTC (rev 275)
@@ -73,6 +73,10 @@
         <artifactId>jbossxacml</artifactId>
       </dependency>
       <dependency>
+        <groupId>javax.persistence</groupId>
+        <artifactId>persistence-api</artifactId>
+      </dependency>
+      <dependency>
         <groupId>sun-jaxws</groupId>
         <artifactId>jaxws-api</artifactId>
         <version>2.1.1</version>

Modified: federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/STSClient.java
===================================================================
--- federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/STSClient.java	2010-06-04 13:29:50 UTC (rev 274)
+++ federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/STSClient.java	2010-06-04 13:46:11 UTC (rev 275)
@@ -40,6 +40,7 @@
 import org.picketlink.identity.federation.core.wstrust.wrappers.RequestSecurityToken;
 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.CancelTargetType;
 import org.picketlink.identity.federation.ws.trust.RenewTargetType;
 import org.picketlink.identity.federation.ws.trust.StatusType;
 import org.picketlink.identity.federation.ws.trust.ValidateTargetType;
@@ -280,6 +281,38 @@
       return false;
    }
 
+   /**
+    * <p>
+    * Cancels the specified security token by sending a WS-Trust cancel message to the STS.
+    * </p>
+    * 
+    * @param securityToken the security token to be canceled.
+    * @return {@code true} if the token has been canceled by the STS; {@code false} otherwise.
+    * @throws WSTrustException if an error occurs while processing the cancel request.
+    */
+   public boolean cancelToken(Element securityToken) throws WSTrustException
+   {
+      // create a WS-Trust cancel request containing the specified token.
+      RequestSecurityToken request = new RequestSecurityToken();
+      request.setRequestType(URI.create(WSTrustConstants.CANCEL_REQUEST));
+      CancelTargetType cancelTarget = new CancelTargetType();
+      cancelTarget.setAny(securityToken);
+      request.setCancelTarget(cancelTarget);
+
+      // marshal the request and send it to the STS.
+      WSTrustJAXBFactory jaxbFactory = WSTrustJAXBFactory.getInstance();
+      DOMSource requestSource = (DOMSource) jaxbFactory.marshallRequestSecurityToken(request);
+      Source response = dispatchLocal.get().invoke(requestSource);
+
+      // get the WS-Trust response and check for presence of the RequestTokenCanceled element.
+      RequestSecurityTokenResponseCollection responseCollection = (RequestSecurityTokenResponseCollection) jaxbFactory
+            .parseRequestSecurityTokenResponse(response);
+      RequestSecurityTokenResponse tokenResponse = responseCollection.getRequestSecurityTokenResponses().get(0);
+      if (tokenResponse.getRequestedTokenCancelled() != null)
+         return true;
+      return false;
+   }
+
    public Dispatch<Source> getDispatch()
    {
       return dispatchLocal.get();

Added: federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/DefaultRevocationRegistry.java
===================================================================
--- federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/DefaultRevocationRegistry.java	                        (rev 0)
+++ federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/DefaultRevocationRegistry.java	2010-06-04 13:46:11 UTC (rev 275)
@@ -0,0 +1,58 @@
+/*
+ * 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.plugins;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * <p>
+ * A simple {@code RevocationRegistry} that keeps the revoked token ids in a memory-only cache. This registry is only
+ * used if no other implementation has been configured and it doesn't persist the revoked ids. For these reasons it is
+ * highly recommended that this implementation be used only in testing scenarios.
+ * </p>
+ * 
+ * @author <a href="mailto:sguilhen at redhat.com">Stefan Guilhen</a>
+ */
+public class DefaultRevocationRegistry implements RevocationRegistry
+{
+   private static Set<String> ids = new HashSet<String>();
+   
+   /*
+    * (non-Javadoc)
+    * @see org.picketlink.identity.federation.core.wstrust.plugins.RevocationRegistry#isRevoked(java.lang.String, java.lang.String)
+    */
+   public boolean isRevoked(String tokenType, String id)
+   {
+      return ids.contains(id);
+   }
+
+   /*
+    * (non-Javadoc)
+    * @see org.picketlink.identity.federation.core.wstrust.plugins.RevocationRegistry#revokeToken(java.lang.String, java.lang.String)
+    */
+   public void revokeToken(String tokenType, String id)
+   {
+      ids.add(id);
+   }
+
+}

Added: federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/FileBasedRevocationRegistry.java
===================================================================
--- federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/FileBasedRevocationRegistry.java	                        (rev 0)
+++ federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/FileBasedRevocationRegistry.java	2010-06-04 13:46:11 UTC (rev 275)
@@ -0,0 +1,191 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2010, 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.plugins;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.log4j.Logger;
+
+/**
+ * <p>
+ * {@code FileBasedRevocationRegistry} is a revocation registry implementation that uses a file to store the ids of the
+ * revoked (canceled) security tokens. By default all ids are stored in $HOME/picketlink-store/sts/revoked.ids but a
+ * different location can be specified through the constructor that takes the file name as a parameter.
+ * </p>
+ * <p>
+ * NOTE: this implementation use a local cache to avoid reading the file system every time a revocation check is made,
+ * making this registry a bad choice for distributed scenarios. Even though the registry file is updated whenever a 
+ * new id is revoked, each node in the cluster will have its own cached view and thus a token that has been canceled by
+ * one node may be accepted by another live node as the caches are not refreshed or synchronized.
+ * </p>
+ * 
+ * @author <a href="mailto:sguilhen at redhat.com">Stefan Guilhen</a>
+ */
+public class FileBasedRevocationRegistry implements RevocationRegistry
+{
+   private static Logger logger = Logger.getLogger(FileBasedRevocationRegistry.class);
+
+   // this set contains the ids of the revoked security tokens.
+   private static Set<String> revokedIds = new HashSet<String>();
+
+   // the file that stores the revoked ids.
+   private File registryFile;
+
+   /**
+    * <p>
+    * Creates an instance of {@code RevocationRegistryFile} that stores the canceled ids in the default
+    * {@code $HOME/picketlink-store/sts/revoked.ids} file.
+    * </p>
+    */
+   public FileBasedRevocationRegistry()
+   {
+      // use the default location registry file location.
+      StringBuilder builder = new StringBuilder();
+      builder.append(System.getProperty("user.home"));
+      builder.append(System.getProperty("file.separator") + "picketlink-store");
+      builder.append(System.getProperty("file.separator") + "sts");
+
+      // check if the $HOME/picketlink-store/sts directory exists.
+      File directory = new File(builder.toString());
+      if (!directory.exists())
+         directory.mkdirs();
+
+      // check if the default registry file exists.
+      this.registryFile = new File(directory, "revoked.ids");
+      if (!this.registryFile.exists())
+      {
+         try
+         {
+            this.registryFile.createNewFile();
+         }
+         catch (IOException ioe)
+         {
+            if (logger.isDebugEnabled())
+               logger.debug("Error creating default registry file: " + ioe.getMessage());
+            ioe.printStackTrace();
+         }
+      }
+
+      // load the revoked ids cache.
+      this.loadRevokedIds();
+   }
+
+   /**
+    * <p>
+    * Creates an instance of {@code RevocationRegistryFile} that stores the canceled ids in specified file.
+    * </p>
+    * 
+    * @param registryFile a {@code String} that indicates the file that must be used to store revoked ids.
+    */
+   public FileBasedRevocationRegistry(String registryFile)
+   {
+      if (registryFile == null)
+         throw new IllegalArgumentException("The revoked ids file cannot be null");
+
+      // check if the specified file exists. If not, create it.
+      this.registryFile = new File(registryFile);
+      if (!this.registryFile.exists())
+      {
+         try
+         {
+            this.registryFile.createNewFile();
+         }
+         catch (IOException ioe)
+         {
+            if (logger.isDebugEnabled())
+               logger.debug("Error creating registry file: " + ioe.getMessage());
+            ioe.printStackTrace();
+         }
+      }
+
+      // load the revoked ids cache.
+      this.loadRevokedIds();
+   }
+
+   /*
+    * (non-Javadoc)
+    * @see org.picketlink.identity.federation.core.wstrust.plugins.RevocationRegistry#isRevoked(java.lang.String, java.lang.String)
+    */
+   public boolean isRevoked(String tokenType, String id)
+   {
+      return revokedIds.contains(id);
+   }
+
+   /*
+    * (non-Javadoc)
+    * @see org.picketlink.identity.federation.core.wstrust.plugins.RevocationRegistry#revokeToken(java.lang.String, java.lang.String)
+    */
+   public synchronized void revokeToken(String tokenType, String id)
+   {
+      try
+      {
+         // write a new line with the revoked id at the end of the file. 
+         BufferedWriter writer = new BufferedWriter(new FileWriter(this.registryFile, true));
+         writer.write(id + "\n");
+         writer.close();
+      }
+      catch (IOException ioe)
+      {
+         if (logger.isDebugEnabled())
+            logger.debug("Error appending content to registry file: " + ioe.getMessage());
+         ioe.printStackTrace();
+      }
+      // add the revoked id to the local cache.
+      revokedIds.add(id);
+
+   }
+
+   /**
+    * <p>
+    * This method loads the ids of the revoked assertions from the registry file. All retrieved ids are set in the
+    * local cache of revoked ids.
+    * </p>
+    */
+   private void loadRevokedIds()
+   {
+      try
+      {
+         // read the file contents and populate the local cache.
+         BufferedReader reader = new BufferedReader(new FileReader(this.registryFile));
+         String id = reader.readLine();
+         while (id != null)
+         {
+            revokedIds.add(id);
+            id = reader.readLine();
+         }
+         reader.close();
+      }
+      catch (IOException ioe)
+      {
+         if (logger.isDebugEnabled())
+            logger.debug("Error opening registry file: " + ioe.getMessage());
+         ioe.printStackTrace();
+      }
+   }
+}

Added: federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/JPABasedRevocationRegistry.java
===================================================================
--- federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/JPABasedRevocationRegistry.java	                        (rev 0)
+++ federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/JPABasedRevocationRegistry.java	2010-06-04 13:46:11 UTC (rev 275)
@@ -0,0 +1,118 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2010, 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.plugins;
+
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.EntityTransaction;
+import javax.persistence.Persistence;
+
+import org.apache.log4j.Logger;
+
+/**
+ * <p>
+ * {@code JPABasedRevocationRegistry} is a revocation registry implementation that uses JPA to store the ids of the
+ * revoked (canceled) security tokens on a database. By default, the JPA configuration has the name {@code picketlink-sts}
+ * but a different configuration name can be specified through the constructor that takes a {@code String} as a parameter.
+ * </p>
+ * <p>
+ * NOTE: this implementation doesn't keep any cache of the security token ids. It performs a JPA query every time the
+ * {@code isRevoked(String id)} method is called. Many JPA providers have internal caching mechanisms that can keep the
+ * data in the cache synchronized with the database and avoid unnecessary trips to the database. This makes this registry
+ * a good choice for clustered environments as any changes to the revocation table made by a node will be visible to
+ * the other nodes.
+ * </p>
+ * 
+ * @author <a href="mailto:sguilhen at redhat.com">Stefan Guilhen</a>
+ */
+public class JPABasedRevocationRegistry implements RevocationRegistry
+{
+
+   private static Logger logger = Logger.getLogger(JPABasedRevocationRegistry.class);
+
+   private EntityManagerFactory factory;
+
+   /**
+    * <p>
+    * Creates an instance of {@code JPABasedRevocationRegistry} that uses the default {@code picketlink-sts} JPA 
+    * configuration to persist the ids of the canceled security tokens.
+    * </p>
+    */
+   public JPABasedRevocationRegistry()
+   {
+      this ("picketlink-sts");
+   }
+   
+   /**
+    * <p>
+    * Creates an instance of {@code JPABasedRevocationRegistry} that uses the specified JPA configuration to persist
+    * the ids of the canceled security tokens.
+    * </p>
+    * 
+    * @param configuration a {@code String} representing the JPA configuration name to be used.
+    */
+   public JPABasedRevocationRegistry(String configuration)
+   {
+      if (configuration == null)
+         throw new IllegalArgumentException("The JPA configuration name cannot be null");
+      this.factory = Persistence.createEntityManagerFactory(configuration);
+   }
+   
+   /*
+    * (non-Javadoc)
+    * @see org.picketlink.identity.federation.core.wstrust.plugins.RevocationRegistry#isRevoked(java.lang.String, java.lang.String)
+    */
+   public boolean isRevoked(String tokenType, String id)
+   {
+      // try to locate a RevokedToken entity with the specified id.
+      EntityManager manager = this.factory.createEntityManager();
+      Object object = manager.find(RevokedToken.class, id);
+      manager.close();
+      
+      return object != null;
+   }
+
+   /*
+    * (non-Javadoc)
+    * @see org.picketlink.identity.federation.core.wstrust.plugins.RevocationRegistry#revokeToken(java.lang.String, java.lang.String)
+    */
+   public void revokeToken(String tokenType, String id)
+   {
+      // if a RevokedToken entity with the specified id doesn't exist in the database, create one and insert it.
+      EntityManager manager = this.factory.createEntityManager();
+      if (manager.find(RevokedToken.class, id) != null)
+      {
+         if (logger.isDebugEnabled())
+            logger.debug("Token with id=" + id + " has already been cancelled");
+      }
+      else
+      {
+         RevokedToken revokedToken = new RevokedToken(tokenType, id);
+         EntityTransaction transaction = manager.getTransaction();
+         transaction.begin();
+         manager.persist(revokedToken);
+         transaction.commit();
+      }
+      manager.close();
+   }
+
+}

Added: federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/RevocationRegistry.java
===================================================================
--- federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/RevocationRegistry.java	                        (rev 0)
+++ federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/RevocationRegistry.java	2010-06-04 13:46:11 UTC (rev 275)
@@ -0,0 +1,54 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2010, 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.plugins;
+
+/**
+ * <p>
+ * A {@code RevocationRegistry} is used to store the ids of revoked (canceled) security tokens.
+ * </p>
+ * 
+ * @author <a href="mailto:sguilhen at redhat.com">Stefan Guilhen</a>
+ */
+public interface RevocationRegistry
+{
+   /**
+    * <p>
+    * Indicates whether the token with the specified id has been revoked or not.
+    * </p>
+    * 
+    * @param tokenType a {@code String} representing the token type.
+    * @param id a {@code String} representing the token id.
+    * @return {@code true} if the specified id has been revoked; {@code false} otherwise.
+    */
+   public boolean isRevoked(String tokenType, String id);
+   
+   /**
+    * <p>
+    * Adds the specified id to the revocation registry. The security token type can be used to distinguish tokens
+    * that may have the same id but that are of different types.
+    * </p>
+    * 
+    * @param tokenType a {@code String} representing the security token type.
+    * @param id the id to registered.
+    */
+   public void revokeToken(String tokenType, String id);
+}

Added: federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/RevokedToken.java
===================================================================
--- federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/RevokedToken.java	                        (rev 0)
+++ federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/RevokedToken.java	2010-06-04 13:46:11 UTC (rev 275)
@@ -0,0 +1,118 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2010, 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.plugins;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+/**
+ * <p>
+ * {@code RevokedToken} is a simple JPA entity used by the {@code JPABasedRevocationRegistry} to persist the ids of
+ * the revoked security tokens.
+ * </p>
+ * 
+ * @author <a href="mailto:sguilhen at redhat.com">Stefan Guilhen</a>
+ */
+ at Entity
+public class RevokedToken
+{
+
+   @Column
+   private String tokenType;
+   
+   @Id
+   private String tokenId;
+
+   /**
+    * <p>
+    * Default constructor.
+    * </p>
+    */
+   public RevokedToken()
+   {
+   }
+
+   /**
+    * <p>
+    * Creates an instance of {@code RevokedToken} with the specified token type and token id.
+    * </p>
+    * 
+    * @param tokenType a {@code String} representing the token type.
+    * @param tokenId a {@code String} representing the token id.
+    */
+   public RevokedToken(String tokenType, String tokenId)
+   {
+      this.tokenType = tokenType;
+      this.tokenId = tokenId;
+   }
+   
+   /**
+    * <p>
+    * Obtains the type of the revoked security token.
+    * </p>
+    * 
+    * @return a {@code String} containing the revoked token type.
+    */
+   public String getTokenType()
+   {
+      return this.tokenType;
+   }
+
+   /**
+    * <p>
+    * Sets the type of revoked security token.
+    * </p>
+    * 
+    * @param tokenType a {@code String} containing the type to be set.
+    */
+   public void setTokenType(String tokenType)
+   {
+      this.tokenType = tokenType;
+   }
+
+   /**
+    * <p>
+    * Obtains the id of the revoked security token.
+    * </p>
+    * 
+    * @return a {@code String} containing the revoked token id.
+    */
+   public String getTokenId()
+   {
+      return this.tokenId;
+   }
+
+   /**
+    * <p>
+    * Sets the id of the revoked security token.
+    * </p>
+    * 
+    * @param tokenId a {@code String} containing the id to be set.
+    */
+   public void setTokenId(String tokenId)
+   {
+      this.tokenId = tokenId;
+   }
+   
+   
+}

Modified: federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/saml/SAML20TokenProvider.java
===================================================================
--- federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/saml/SAML20TokenProvider.java	2010-06-04 13:29:50 UTC (rev 274)
+++ federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/saml/SAML20TokenProvider.java	2010-06-04 13:46:11 UTC (rev 275)
@@ -21,19 +21,12 @@
  */
 package org.picketlink.identity.federation.core.wstrust.plugins.saml;
 
-import java.io.BufferedReader;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
 import java.security.Principal;
+import java.security.PrivilegedActionException;
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 import javax.xml.bind.JAXBException;
 import javax.xml.namespace.QName;
@@ -50,6 +43,10 @@
 import org.picketlink.identity.federation.core.wstrust.WSTrustException;
 import org.picketlink.identity.federation.core.wstrust.WSTrustRequestContext;
 import org.picketlink.identity.federation.core.wstrust.WSTrustUtil;
+import org.picketlink.identity.federation.core.wstrust.plugins.DefaultRevocationRegistry;
+import org.picketlink.identity.federation.core.wstrust.plugins.RevocationRegistry;
+import org.picketlink.identity.federation.core.wstrust.plugins.FileBasedRevocationRegistry;
+import org.picketlink.identity.federation.core.wstrust.plugins.JPABasedRevocationRegistry;
 import org.picketlink.identity.federation.core.wstrust.wrappers.Lifetime;
 import org.picketlink.identity.federation.saml.v2.assertion.AssertionType;
 import org.picketlink.identity.federation.saml.v2.assertion.AudienceRestrictionType;
@@ -77,13 +74,11 @@
 
    private static Logger logger = Logger.getLogger(SAML20TokenProvider.class);
 
-   private static final String CANCELED_IDS_FILE = "CanceledIdsFile";
+   private static final String REVOCATION_REGISTRY = "RevocationRegistry";
 
-   // this set contains the ids of the assertions that have been canceled.
-   private Set<String> cancelledIds;
+   private static final String REVOCATION_REGISTRY_FILE = "RevocationRegistryFile";
 
-   // file used to store the ids of the canceled assertions.
-   private File canceledIdsFile;
+   private RevocationRegistry revocationRegistry;
 
    private Map<String, String> properties;
 
@@ -95,17 +90,55 @@
    public void initialize(Map<String, String> properties)
    {
       this.properties = properties;
-      this.cancelledIds = new HashSet<String>();
 
-      // set up the canceled ids cache if the file that contains the canceled assertions has been specified.
-      String file = this.properties.get(CANCELED_IDS_FILE);
-      if (file == null && logger.isDebugEnabled())
-         logger.debug("File to store canceled ids has not been specified: ids will not be persisted!");
-      else if (file != null)
+      // check if a revocation registry option has been set.
+      String registryOption = this.properties.get(REVOCATION_REGISTRY);
+      if (registryOption == null)
       {
-         this.canceledIdsFile = new File(file);
-         this.loadCanceledIds();
+         if (logger.isDebugEnabled())
+            logger.debug("Revocation registry option not specified: cancelled ids will not be persisted!");
+         this.revocationRegistry = new DefaultRevocationRegistry();
       }
+      else
+      {
+         // if a file is to be used as registry, check if the user has specified the file name.
+         if ("FILE".equalsIgnoreCase(registryOption))
+         {
+            String registryFile = this.properties.get(REVOCATION_REGISTRY_FILE);
+            if (registryFile != null)
+               this.revocationRegistry = new FileBasedRevocationRegistry(registryFile);
+            else
+               this.revocationRegistry = new FileBasedRevocationRegistry();
+         }
+         // another option is to use the default JPA registry to store the revoked ids.
+         else if ("JPA".equalsIgnoreCase(registryOption))
+         {
+            this.revocationRegistry = new JPABasedRevocationRegistry();
+         }
+         // the user has specified its own registry implementation class.
+         else
+         {
+            try
+            {
+               Object object = SecurityActions.instantiateClass(registryOption);
+               if (object instanceof RevocationRegistry)
+                  this.revocationRegistry = (RevocationRegistry) object;
+               else
+               {
+                  if (logger.isDebugEnabled())
+                     logger.debug(registryOption + " is not an instance of RevocationRegistry - using default registry");
+                  this.revocationRegistry = new DefaultRevocationRegistry();
+               }
+            }
+            catch (PrivilegedActionException pae)
+            {
+               if (logger.isDebugEnabled())
+                  logger.debug("Error instantiating revocation registry class - using default registry");
+               pae.printStackTrace();
+               this.revocationRegistry = new DefaultRevocationRegistry();
+            }
+         }
+      }
    }
 
    /*
@@ -126,7 +159,7 @@
 
       // get the assertion ID and add it to the canceled assertions set.
       String assertionId = assertionElement.getAttribute("ID");
-      this.storeCanceledId(assertionId);
+      this.revocationRegistry.revokeToken(SAMLUtil.SAML2_TOKEN_TYPE, assertionId);
    }
 
    /*
@@ -242,8 +275,9 @@
       }
 
       // canceled assertions cannot be renewed.
-      if (this.cancelledIds.contains(oldAssertion.getID()))
-         throw new WSTrustException("Assertion with id " + oldAssertion.getID() + " is canceled and cannot be renewed");
+      if (this.revocationRegistry.isRevoked(SAMLUtil.SAML2_TOKEN_TYPE, oldAssertion.getID()))
+         throw new WSTrustException("Assertion with id " + oldAssertion.getID()
+               + " has been canceled and cannot be renewed");
 
       // adjust the lifetime for the renewed assertion.
       ConditionsType conditions = oldAssertion.getConditions();
@@ -319,10 +353,10 @@
       }
 
       // check if the assertion has been canceled before.
-      if (this.cancelledIds.contains(assertion.getID()))
+      if (this.revocationRegistry.isRevoked(SAMLUtil.SAML2_TOKEN_TYPE, assertion.getID()))
       {
          code = WSTrustConstants.STATUS_CODE_INVALID;
-         reason = "Validation failure: assertion with id " + assertion.getID() + " is canceled";
+         reason = "Validation failure: assertion with id " + assertion.getID() + " has been canceled";
       }
 
       // check the assertion lifetime.
@@ -361,65 +395,4 @@
             && WSTrustConstants.SAML2_ASSERTION_NS.equals(element.getNamespaceURI());
    }
 
-   /**
-    * <p>
-    * This method loads the ids of the canceled assertions from the file that has been configured for this provider.
-    * All retrieved ids are set in the local cache of canceled ids.
-    * </p>
-    */
-   private void loadCanceledIds()
-   {
-      try
-      {
-         if (!this.canceledIdsFile.exists())
-         {
-            if (logger.isDebugEnabled())
-               logger.debug("File " + this.canceledIdsFile.getCanonicalPath() + " doesn't exist and will be created");
-            this.canceledIdsFile.createNewFile();
-         }
-         // read the file contents and populate the local cache.
-         BufferedReader reader = new BufferedReader(new FileReader(this.canceledIdsFile));
-         String id = reader.readLine();
-         while(id != null)
-         {
-            this.cancelledIds.add(id);
-            id = reader.readLine();
-         }
-         reader.close();
-      }
-      catch (IOException ioe)
-      {
-         if (logger.isDebugEnabled())
-            logger.debug("Error opening canceled ids file: " + ioe.getMessage());
-         ioe.printStackTrace();
-      }
-   }
-   
-   /**
-    * <p>
-    * Stores the specified id in the cache of canceled ids. If a canceled ids file has been configured for this 
-    * provider, the id will also be written to the end of the file.
-    * </p>
-    * 
-    * @param id a {@code String} representing the canceled id that must be stored.
-    */
-   public synchronized void storeCanceledId(String id)
-   {
-      if (this.canceledIdsFile != null)
-      {
-         try
-         {
-            // write a new line with the canceled id at the end of the file. 
-            BufferedWriter writer = new BufferedWriter(new FileWriter(this.canceledIdsFile, true));
-            writer.write(id + "\n");
-            writer.close();
-         }
-         catch (IOException e)
-         {
-            e.printStackTrace();
-         }
-      }
-      // add the canceled id to the local cache.
-      this.cancelledIds.add(id);
-   }
 }
\ No newline at end of file

Added: federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/saml/SecurityActions.java
===================================================================
--- federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/saml/SecurityActions.java	                        (rev 0)
+++ federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/saml/SecurityActions.java	2010-06-04 13:46:11 UTC (rev 275)
@@ -0,0 +1,108 @@
+/*
+ * 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.plugins.saml;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+
+/**
+ * <p>
+ * Utility class that executes actions such as creating a class in privileged blocks.
+ * </p>
+ * 
+ * @author <a href="mailto:sguilhen at redhat.com">Stefan Guilhen</a>
+ */
+class SecurityActions
+{
+
+   /**
+    * <p>
+    * Gets the thread context class loader using a privileged block.
+    * </p>
+    * 
+    * @return a reference to the thread context {@code ClassLoader}.
+    */
+   static ClassLoader getContextClassLoader()
+   {
+      return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>()
+      {
+         public ClassLoader run()
+         {
+            return Thread.currentThread().getContextClassLoader();
+         }
+      });
+   }
+
+   /**
+    * <p>
+    * Loads a class using the thread context class loader in a privileged block.
+    * </p>
+    * 
+    * @param name the fully-qualified name of the class to be loaded.
+    * @return a reference to the loaded {@code Class}.
+    * @throws PrivilegedActionException if an error occurs while loading the class. This exception wraps the real cause
+    *             of the error, so classes using this method must perform a {@code getCause()} in order to get a
+    *             reference to the root of the error.
+    */
+   static Class<?> loadClass(final String name) throws PrivilegedActionException
+   {
+      return AccessController.doPrivileged(new PrivilegedExceptionAction<Class<?>>()
+      {
+         public Class<?> run() throws PrivilegedActionException
+         {
+            try
+            {
+               return getContextClassLoader().loadClass(name);
+            }
+            catch (Exception e)
+            {
+               throw new PrivilegedActionException(e);
+            }
+         }
+      });
+   }
+
+   /**
+    * <p>
+    * Creates an instance of the specified class in a privileged block. The class must define a default constructor.
+    * </p>
+    * 
+    * @param className the fully-qualified name of the class to be instantiated.
+    * @return a reference to the instantiated {@code Object}.
+    * @throws PrivilegedActionException if an error occurs while instantiating the class. This exception wraps the real
+    *             cause of the error, so classes using this method must perform a {@code getCause()} in order to get a
+    *             reference to the root of the error.
+    */
+   static Object instantiateClass(final String className) throws PrivilegedActionException
+   {
+      return AccessController.doPrivileged(new PrivilegedExceptionAction<Object>()
+      {
+         public Object run() throws Exception
+         {
+            Class<?> objectClass = loadClass(className);
+            return objectClass.newInstance();
+         }
+      });
+   }
+}
\ No newline at end of file

Modified: federation/trunk/picketlink-fed-core/src/test/java/org/picketlink/test/identity/federation/core/wstrust/PicketLinkSTSUnitTestCase.java
===================================================================
--- federation/trunk/picketlink-fed-core/src/test/java/org/picketlink/test/identity/federation/core/wstrust/PicketLinkSTSUnitTestCase.java	2010-06-04 13:29:50 UTC (rev 274)
+++ federation/trunk/picketlink-fed-core/src/test/java/org/picketlink/test/identity/federation/core/wstrust/PicketLinkSTSUnitTestCase.java	2010-06-04 13:46:11 UTC (rev 275)
@@ -807,7 +807,7 @@
       assertNotNull("Unexpected null status", status);
       assertEquals("Unexpected status code", WSTrustConstants.STATUS_CODE_INVALID, status.getCode());
       assertEquals("Unexpected status reason", "Validation failure: assertion with id " + assertion.getAttribute("ID")
-            + " is canceled", status.getReason());
+            + " has been canceled", status.getReason());
 
       // now try to renew the canceled assertion.
       request = this.createRequest("renewcontext", WSTrustConstants.RENEW_REQUEST, null, null);
@@ -825,7 +825,7 @@
       {
          assertTrue("Unexpected cause type", we.getCause() instanceof WSTrustException);
          assertEquals("Unexpected exception message", "Assertion with id " + assertion.getAttribute("ID")
-               + " is canceled and cannot be renewed", we.getCause().getMessage());
+               + " has been canceled and cannot be renewed", we.getCause().getMessage());
       }
    }
 



More information about the jboss-cvs-commits mailing list