Author: sguilhen(a)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@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@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@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@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@redhat.com">Stefan Guilhen</a>
+ */
+@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@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());
}
}