Author: sguilhen(a)redhat.com
Date: 2011-07-14 18:24:22 -0400 (Thu, 14 Jul 2011)
New Revision: 1091
Added:
federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/saml/SAML11TokenProvider.java
Modified:
federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/sts/AbstractSecurityTokenProvider.java
Log:
Fixed logic in AbstractSecurityTokenProvider that prevented the revocation registries from
being loaded.
Modified:
federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/sts/AbstractSecurityTokenProvider.java
===================================================================
---
federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/sts/AbstractSecurityTokenProvider.java 2011-07-14
15:04:56 UTC (rev 1090)
+++
federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/sts/AbstractSecurityTokenProvider.java 2011-07-14
22:24:22 UTC (rev 1091)
@@ -107,66 +107,64 @@
pae.printStackTrace();
}
}
+ }
+ if (this.tokenRegistry == null)
+ tokenRegistry = new DefaultTokenRegistry();
- if (this.tokenRegistry == null)
- tokenRegistry = new DefaultTokenRegistry();
-
- // check if a revocation registry option has been set.
- String registryOption = this.properties.get(REVOCATION_REGISTRY);
- if (registryOption == null)
+ // check if a revocation registry option has been set.
+ String registryOption = this.properties.get(REVOCATION_REGISTRY);
+ if (registryOption == null)
+ {
+ if (logger.isDebugEnabled())
+ logger.debug("Revocation registry option not specified: cancelled ids
will not be persisted!");
+ }
+ else
+ {
+ // if a file is to be used as registry, check if the user has specified the file
name.
+ if ("FILE".equalsIgnoreCase(registryOption))
{
- if (logger.isDebugEnabled())
- logger.debug("Revocation registry option not specified: cancelled ids
will not be persisted!");
+ 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))
+ {
+ String configuration = this.properties.get(REVOCATION_REGISTRY_JPA_CONFIG);
+ if (configuration != null)
+ this.revocationRegistry = new JPABasedRevocationRegistry(configuration);
+ else
+ this.revocationRegistry = new JPABasedRevocationRegistry();
+ }
+ // the user has specified its own registry implementation class.
else
{
- // if a file is to be used as registry, check if the user has specified the
file name.
- if ("FILE".equalsIgnoreCase(registryOption))
+ try
{
- 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))
- {
- String configuration =
this.properties.get(REVOCATION_REGISTRY_JPA_CONFIG);
- if (configuration != null)
- this.revocationRegistry = new
JPABasedRevocationRegistry(configuration);
- else
- this.revocationRegistry = new JPABasedRevocationRegistry();
- }
- // the user has specified its own registry implementation class.
- else
- {
- try
+ Class<?> clazz = SecurityActions.loadClass(getClass(),
registryOption);
+ if (clazz != null)
{
- Class<?> clazz = SecurityActions.loadClass(getClass(),
registryOption);
- if (clazz != null)
+ Object object = clazz.newInstance();
+ if (object instanceof RevocationRegistry)
+ this.revocationRegistry = (RevocationRegistry) object;
+ else
{
- Object object = clazz.newInstance();
- if (object instanceof RevocationRegistry)
- this.revocationRegistry = (RevocationRegistry) object;
- else
- {
- logger.warn(registryOption
- + " is not an instance of RevocationRegistry - using
default registry");
- }
+ logger.warn(registryOption
+ + " is not an instance of RevocationRegistry - using
default registry");
}
}
- catch (Exception pae)
- {
- logger.warn("Error instantiating revocation registry class - using
default registry");
- pae.printStackTrace();
- }
}
+ catch (Exception pae)
+ {
+ logger.warn("Error instantiating revocation registry class - using
default registry");
+ pae.printStackTrace();
+ }
}
-
- if (this.revocationRegistry == null)
- this.revocationRegistry = new DefaultRevocationRegistry();
}
- }
+ if (this.revocationRegistry == null)
+ this.revocationRegistry = new DefaultRevocationRegistry();
+ }
}
\ No newline at end of file
Added:
federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/saml/SAML11TokenProvider.java
===================================================================
---
federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/saml/SAML11TokenProvider.java
(rev 0)
+++
federation/trunk/picketlink-fed-core/src/main/java/org/picketlink/identity/federation/core/wstrust/plugins/saml/SAML11TokenProvider.java 2011-07-14
22:24:22 UTC (rev 1091)
@@ -0,0 +1,383 @@
+package org.picketlink.identity.federation.core.wstrust.plugins.saml;
+
+import java.net.URI;
+import java.security.Principal;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.namespace.QName;
+
+import org.apache.log4j.Logger;
+import org.picketlink.identity.federation.core.exceptions.ProcessingException;
+import org.picketlink.identity.federation.core.interfaces.ProtocolContext;
+import org.picketlink.identity.federation.core.interfaces.SecurityTokenProvider;
+import org.picketlink.identity.federation.core.saml.v1.SAML11Constants;
+import org.picketlink.identity.federation.core.saml.v2.common.IDGenerator;
+import org.picketlink.identity.federation.core.saml.v2.constants.JBossSAMLConstants;
+import org.picketlink.identity.federation.core.saml.v2.util.AssertionUtil;
+import org.picketlink.identity.federation.core.sts.AbstractSecurityTokenProvider;
+import org.picketlink.identity.federation.core.wstrust.SecurityToken;
+import org.picketlink.identity.federation.core.wstrust.StandardSecurityToken;
+import org.picketlink.identity.federation.core.wstrust.WSTrustConstants;
+import org.picketlink.identity.federation.core.wstrust.WSTrustRequestContext;
+import org.picketlink.identity.federation.core.wstrust.WSTrustUtil;
+import org.picketlink.identity.federation.core.wstrust.wrappers.Lifetime;
+import org.picketlink.identity.federation.saml.v1.assertion.SAML11AssertionType;
+import
org.picketlink.identity.federation.saml.v1.assertion.SAML11AudienceRestrictionCondition;
+import
org.picketlink.identity.federation.saml.v1.assertion.SAML11AuthenticationStatementType;
+import org.picketlink.identity.federation.saml.v1.assertion.SAML11ConditionsType;
+import org.picketlink.identity.federation.saml.v1.assertion.SAML11NameIdentifierType;
+import org.picketlink.identity.federation.saml.v1.assertion.SAML11StatementAbstractType;
+import
org.picketlink.identity.federation.saml.v1.assertion.SAML11SubjectConfirmationType;
+import org.picketlink.identity.federation.saml.v1.assertion.SAML11SubjectType;
+import org.picketlink.identity.federation.ws.policy.AppliesTo;
+import org.picketlink.identity.federation.ws.trust.RequestedReferenceType;
+import org.picketlink.identity.federation.ws.trust.StatusType;
+import org.picketlink.identity.federation.ws.wss.secext.KeyIdentifierType;
+import org.picketlink.identity.xmlsec.w3.xmldsig.KeyInfoType;
+import org.w3c.dom.Element;
+
+public class SAML11TokenProvider extends AbstractSecurityTokenProvider
+{
+ protected static Logger logger = Logger.getLogger(SAML11TokenProvider.class);
+
+ /*
+ * (non-Javadoc)
+ *
+ *
@seeorg.picketlink.identity.federation.core.interfaces.SecurityTokenProvider#cancelToken(org.picketlink.identity.
+ * federation.core.interfaces.ProtocolContext)
+ */
+ @Override
+ public void cancelToken(ProtocolContext context) throws ProcessingException
+ {
+ if (!(context instanceof WSTrustRequestContext))
+ return;
+
+ WSTrustRequestContext wstContext = (WSTrustRequestContext) context;
+
+ // get the SAML assertion that will be canceled.
+ Element token = wstContext.getRequestSecurityToken().getCancelTargetElement();
+ if (token == null)
+ throw new ProcessingException("Invalid cancel request: missing required
CancelTarget");
+ Element assertionElement = (Element) token.getFirstChild();
+ if (!this.isSAMLAssertion(assertionElement))
+ throw new ProcessingException("CancelTarget doesn't not contain a
SAMLV1.1 assertion");
+
+ // get the assertion ID and add it to the canceled assertions set.
+ String assertionId = assertionElement.getAttribute("AssertionID");
+ this.revocationRegistry.revokeToken(SAMLUtil.SAML11_TOKEN_TYPE, assertionId);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ *
org.picketlink.identity.federation.core.interfaces.SecurityTokenProvider#issueToken(org.picketlink.identity.federation
+ * .core.interfaces.ProtocolContext)
+ */
+ @Override
+ public void issueToken(ProtocolContext context) throws ProcessingException
+ {
+ if (!(context instanceof WSTrustRequestContext))
+ return;
+
+ WSTrustRequestContext wstContext = (WSTrustRequestContext) context;
+ // generate an id for the new assertion.
+ String assertionID = IDGenerator.create("ID_");
+
+ // lifetime and audience restrictions.
+ Lifetime lifetime = wstContext.getRequestSecurityToken().getLifetime();
+ SAML11AudienceRestrictionCondition restriction = null;
+ AppliesTo appliesTo = wstContext.getRequestSecurityToken().getAppliesTo();
+ if (appliesTo != null)
+ {
+ restriction = new SAML11AudienceRestrictionCondition();
+ restriction.add(URI.create(WSTrustUtil.parseAppliesTo(appliesTo)));
+ }
+ SAML11ConditionsType conditions = new SAML11ConditionsType();
+ conditions.setNotBefore(lifetime.getCreated());
+ conditions.setNotOnOrAfter(lifetime.getExpires());
+ conditions.add(restriction);
+
+ // the assertion principal (default is caller principal)
+ Principal principal = wstContext.getCallerPrincipal();
+
+ String confirmationMethod = null;
+ KeyInfoType keyInfoType = null;
+ // if there is a on-behalf-of principal, we have the sender vouches confirmation
method.
+ if (wstContext.getOnBehalfOfPrincipal() != null)
+ {
+ principal = wstContext.getOnBehalfOfPrincipal();
+ confirmationMethod = SAMLUtil.SAML11_SENDER_VOUCHES_URI;
+ }
+ // if there is a proof-of-possession token in the context, we have the holder of
key confirmation method.
+ else if (wstContext.getProofTokenInfo() != null)
+ {
+ confirmationMethod = SAMLUtil.SAML11_HOLDER_OF_KEY_URI;
+ keyInfoType = wstContext.getProofTokenInfo();
+ }
+ else
+ confirmationMethod = SAMLUtil.SAML11_BEARER_URI;
+
+ SAML11SubjectConfirmationType subjectConfirmation = new
SAML11SubjectConfirmationType();
+ subjectConfirmation.addConfirmationMethod(URI.create(confirmationMethod));
+ // TODO: set the key info.
+
+ // create a subject using the caller principal or on-behalf-of principal.
+ String subjectName = principal == null ? "ANONYMOUS" :
principal.getName();
+ SAML11NameIdentifierType nameId = new SAML11NameIdentifierType(subjectName);
+ nameId.setFormat(URI.create(SAML11Constants.FORMAT_UNSPECIFIED));
+ SAML11SubjectType subject = new SAML11SubjectType();
+ subject.setChoice(new SAML11SubjectType.SAML11SubjectTypeChoice(nameId));
+ subject.setSubjectConfirmation(subjectConfirmation);
+
+ // add the subject to an auth statement.
+ SAML11AuthenticationStatementType authStatement = new
SAML11AuthenticationStatementType(URI
+ .create("urn:picketlink:auth"), lifetime.getCreated());
+ authStatement.setSubject(subject);
+
+ // TODO: add attribute statements.
+
+ // create the SAML assertion.
+ SAML11AssertionType assertion = new SAML11AssertionType(assertionID,
lifetime.getCreated());
+ assertion.add(authStatement);
+ assertion.setConditions(conditions);
+ assertion.setIssuer(wstContext.getTokenIssuer());
+
+ // convert the constructed assertion to element.
+ Element assertionElement = null;
+ try
+ {
+ assertionElement = SAMLUtil.toElement(assertion);
+ }
+ catch (Exception e)
+ {
+ throw new ProcessingException("Failed to marshall SAMLV1.1 assertion",
e);
+ }
+ SecurityToken token = new
StandardSecurityToken(wstContext.getRequestSecurityToken().getTokenType().toString(),
+ assertionElement, assertionID);
+ wstContext.setSecurityToken(token);
+
+ // set the SAML assertion attached reference.
+ KeyIdentifierType keyIdentifier =
WSTrustUtil.createKeyIdentifier(SAMLUtil.SAML11_VALUE_TYPE, "#" + assertionID);
+ Map<QName, String> attributes = new HashMap<QName, String>();
+ attributes.put(new QName(WSTrustConstants.WSSE11_NS, "TokenType",
WSTrustConstants.WSSE.PREFIX_11),
+ SAMLUtil.SAML11_TOKEN_TYPE);
+ RequestedReferenceType attachedReference =
WSTrustUtil.createRequestedReference(keyIdentifier, attributes);
+ wstContext.setAttachedReference(attachedReference);
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ *
org.picketlink.identity.federation.core.interfaces.SecurityTokenProvider#renewToken(org.picketlink.identity.federation
+ * .core.interfaces.ProtocolContext)
+ */
+ @Override
+ public void renewToken(ProtocolContext context) throws ProcessingException
+ {
+ if (!(context instanceof WSTrustRequestContext))
+ return;
+
+ WSTrustRequestContext wstContext = (WSTrustRequestContext) context;
+ // get the specified assertion that must be renewed.
+ Element token = wstContext.getRequestSecurityToken().getRenewTargetElement();
+ if (token == null)
+ throw new ProcessingException("Invalid renew request: missing required
RenewTarget");
+ Element oldAssertionElement = (Element) token.getFirstChild();
+ if (!this.isSAMLAssertion(oldAssertionElement))
+ throw new ProcessingException("RenewTarget doesn't not contain a
SAMLV1.1 assertion");
+
+ // get the JAXB representation of the old assertion.
+ SAML11AssertionType oldAssertion = null;
+ try
+ {
+ oldAssertion = SAMLUtil.saml11FromElement(oldAssertionElement);
+ }
+ catch (Exception je)
+ {
+ throw new ProcessingException("Error unmarshalling assertion", je);
+ }
+
+ // canceled assertions cannot be renewed.
+ if (this.revocationRegistry.isRevoked(SAMLUtil.SAML11_TOKEN_TYPE,
oldAssertion.getID()))
+ throw new ProcessingException("SAMLV1.1 Assertion with id " +
oldAssertion.getID()
+ + " has been canceled and cannot be renewed");
+
+ // adjust the lifetime for the renewed assertion.
+ SAML11ConditionsType conditions = oldAssertion.getConditions();
+
conditions.setNotBefore(wstContext.getRequestSecurityToken().getLifetime().getCreated());
+
conditions.setNotOnOrAfter(wstContext.getRequestSecurityToken().getLifetime().getExpires());
+
+ // create a new unique ID for the renewed assertion.
+ String assertionID = IDGenerator.create("ID_");
+
+ // get the list of all assertion statements - should include the auth statement
that contains the subject.
+ List<SAML11StatementAbstractType> statements = new
ArrayList<SAML11StatementAbstractType>();
+ statements.addAll(oldAssertion.getStatements());
+
+ // create the new assertion.
+ SAML11AssertionType newAssertion = new SAML11AssertionType(assertionID,
conditions.getNotBefore());
+ newAssertion.addAllStatements(statements);
+ newAssertion.setConditions(conditions);
+ newAssertion.setIssuer(wstContext.getTokenIssuer());
+
+ // create a security token with the new assertion.
+ Element assertionElement = null;
+ try
+ {
+ assertionElement = SAMLUtil.toElement(newAssertion);
+ }
+ catch (Exception e)
+ {
+ throw new ProcessingException("Failed to marshall SAMLV1.1 assertion",
e);
+ }
+ SecurityToken securityToken = new
StandardSecurityToken(wstContext.getRequestSecurityToken().getTokenType()
+ .toString(), assertionElement, assertionID);
+ wstContext.setSecurityToken(securityToken);
+
+ // set the SAML assertion attached reference.
+ KeyIdentifierType keyIdentifier =
WSTrustUtil.createKeyIdentifier(SAMLUtil.SAML11_VALUE_TYPE, "#" + assertionID);
+ Map<QName, String> attributes = new HashMap<QName, String>();
+ attributes.put(new QName(WSTrustConstants.WSSE11_NS, "TokenType"),
SAMLUtil.SAML11_TOKEN_TYPE);
+ RequestedReferenceType attachedReference =
WSTrustUtil.createRequestedReference(keyIdentifier, attributes);
+ wstContext.setAttachedReference(attachedReference);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ *
org.picketlink.identity.federation.core.interfaces.SecurityTokenProvider#validateToken(org.picketlink.identity
+ * .federation.core.interfaces.ProtocolContext)
+ */
+ @Override
+ public void validateToken(ProtocolContext context) throws ProcessingException
+ {
+ if (!(context instanceof WSTrustRequestContext))
+ return;
+
+ WSTrustRequestContext wstContext = (WSTrustRequestContext) context;
+ if (logger.isTraceEnabled())
+ logger.trace("SAML V1.1 token validation started");
+
+ // get the SAML assertion that must be validated.
+ Element token = wstContext.getRequestSecurityToken().getValidateTargetElement();
+ if (token == null)
+ throw new ProcessingException("Bad validate request: missing required
ValidateTarget");
+
+ String code = WSTrustConstants.STATUS_CODE_VALID;
+ String reason = "SAMLV1.1 Assertion successfuly validated";
+
+ SAML11AssertionType assertion = null;
+ Element assertionElement = (Element) token.getFirstChild();
+ if (!this.isSAMLAssertion(assertionElement))
+ {
+ code = WSTrustConstants.STATUS_CODE_INVALID;
+ reason = "Validation failure: supplied token is not a SAMLV1.1
Assertion";
+ }
+ else
+ {
+ try
+ {
+ assertion = SAMLUtil.saml11FromElement(assertionElement);
+ }
+ catch (Exception e)
+ {
+ throw new ProcessingException("Unmarshalling error:", e);
+ }
+ }
+
+ // check if the assertion has been canceled before.
+ if (this.revocationRegistry.isRevoked(SAMLUtil.SAML11_TOKEN_TYPE,
assertion.getID()))
+ {
+ code = WSTrustConstants.STATUS_CODE_INVALID;
+ reason = "Validation failure: assertion with id " + assertion.getID()
+ " has been canceled";
+ }
+
+ // check the assertion lifetime.
+ try
+ {
+ if (AssertionUtil.hasExpired(assertion))
+ {
+ code = WSTrustConstants.STATUS_CODE_INVALID;
+ reason = "Validation failure: assertion expired or used before its
lifetime period";
+ }
+ }
+ catch (Exception ce)
+ {
+ code = WSTrustConstants.STATUS_CODE_INVALID;
+ reason = "Validation failure: unable to verify assertion lifetime: " +
ce.getMessage();
+ }
+
+ // construct the status and set it on the request context.
+ StatusType status = new StatusType();
+ status.setCode(code);
+ status.setReason(reason);
+ wstContext.setStatus(status);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
org.picketlink.identity.federation.core.interfaces.SecurityTokenProvider#family()
+ */
+ @Override
+ public String family()
+ {
+ return SecurityTokenProvider.FAMILY_TYPE.WS_TRUST.toString();
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
org.picketlink.identity.federation.core.interfaces.SecurityTokenProvider#getSupportedQName()
+ */
+ @Override
+ public QName getSupportedQName()
+ {
+ return new QName(tokenType(), JBossSAMLConstants.ASSERTION.get());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
org.picketlink.identity.federation.core.interfaces.SecurityTokenProvider#supports(java.lang.String)
+ */
+ @Override
+ public boolean supports(String namespace)
+ {
+ return WSTrustConstants.BASE_NAMESPACE.equals(namespace);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
org.picketlink.identity.federation.core.interfaces.SecurityTokenProvider#tokenType()
+ */
+ @Override
+ public String tokenType()
+ {
+ return SAMLUtil.SAML11_TOKEN_TYPE;
+ }
+
+ /**
+ * <p>
+ * Checks whether the specified element is a SAMLV1.1 assertion or not.
+ * </p>
+ *
+ * @param element
+ * the {@code Element} being verified.
+ * @return {@code true} if the element is a SAMLV1.1 assertion; {@code false}
otherwise.
+ */
+ private boolean isSAMLAssertion(Element element)
+ {
+ return element == null ? false :
"Assertion".equals(element.getLocalName())
+ &&
SAML11Constants.ASSERTION_11_NSURI.equals(element.getNamespaceURI());
+ }
+
+}