Author: bmozaffa(a)redhat.com
Date: 2010-06-11 15:25:03 -0400 (Fri, 11 Jun 2010)
New Revision: 296
Added:
federation/trunk/picketlink-bindings-jboss/src/main/java/org/picketlink/identity/federation/bindings/jboss/auth/mapping/
federation/trunk/picketlink-bindings-jboss/src/main/java/org/picketlink/identity/federation/bindings/jboss/auth/mapping/STSGroupMappingProvider.java
federation/trunk/picketlink-bindings-jboss/src/main/java/org/picketlink/identity/federation/bindings/jboss/auth/mapping/STSPrincipalMappingProvider.java
federation/trunk/picketlink-bindings-jboss/src/test/java/org/
federation/trunk/picketlink-bindings-jboss/src/test/java/org/picketlink/
federation/trunk/picketlink-bindings-jboss/src/test/java/org/picketlink/identity/
federation/trunk/picketlink-bindings-jboss/src/test/java/org/picketlink/identity/federation/
federation/trunk/picketlink-bindings-jboss/src/test/java/org/picketlink/identity/federation/bindings/
federation/trunk/picketlink-bindings-jboss/src/test/java/org/picketlink/identity/federation/bindings/jboss/
federation/trunk/picketlink-bindings-jboss/src/test/java/org/picketlink/identity/federation/bindings/jboss/auth/
federation/trunk/picketlink-bindings-jboss/src/test/java/org/picketlink/identity/federation/bindings/jboss/auth/STSMappingProviderUnitTestCase.java
Modified:
federation/trunk/picketlink-bindings-jboss/pom.xml
Log:
PLFED-87: STS Login Modules now look for any configured principal and role mapping
providers in the security domain and use them to obtain user id and role and populate the
Subject with them. These two OOTB mapping providers extract and return the user's id
and roles for use in a JBoss environment.
Modified: federation/trunk/picketlink-bindings-jboss/pom.xml
===================================================================
--- federation/trunk/picketlink-bindings-jboss/pom.xml 2010-06-11 19:13:30 UTC (rev 295)
+++ federation/trunk/picketlink-bindings-jboss/pom.xml 2010-06-11 19:25:03 UTC (rev 296)
@@ -81,6 +81,12 @@
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.jboss.javaee</groupId>
+ <artifactId>jboss-javaee</artifactId>
+ <version>5.0.0.CR1</version>
+ <scope>provided</scope>
+ </dependency>
</dependencies>
<reporting>
Added:
federation/trunk/picketlink-bindings-jboss/src/main/java/org/picketlink/identity/federation/bindings/jboss/auth/mapping/STSGroupMappingProvider.java
===================================================================
---
federation/trunk/picketlink-bindings-jboss/src/main/java/org/picketlink/identity/federation/bindings/jboss/auth/mapping/STSGroupMappingProvider.java
(rev 0)
+++
federation/trunk/picketlink-bindings-jboss/src/main/java/org/picketlink/identity/federation/bindings/jboss/auth/mapping/STSGroupMappingProvider.java 2010-06-11
19:25:03 UTC (rev 296)
@@ -0,0 +1,186 @@
+package org.picketlink.identity.federation.bindings.jboss.auth.mapping;
+
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.JAXBException;
+
+import org.apache.log4j.Logger;
+import org.jboss.security.identity.RoleGroup;
+import org.jboss.security.identity.plugins.SimpleRole;
+import org.jboss.security.identity.plugins.SimpleRoleGroup;
+import org.jboss.security.mapping.MappingProvider;
+import org.jboss.security.mapping.MappingResult;
+import
org.picketlink.identity.federation.bindings.jboss.auth.SAML20TokenRoleAttributeProvider;
+import org.picketlink.identity.federation.core.wstrust.auth.AbstractSTSLoginModule;
+import org.picketlink.identity.federation.core.wstrust.plugins.saml.SAMLUtil;
+import org.picketlink.identity.federation.saml.v2.assertion.AssertionType;
+import org.picketlink.identity.federation.saml.v2.assertion.AttributeStatementType;
+import org.picketlink.identity.federation.saml.v2.assertion.AttributeType;
+import org.picketlink.identity.federation.saml.v2.assertion.StatementAbstractType;
+import org.w3c.dom.Element;
+
+/**
+ * <p>
+ * This mapping provider looks at the role attributes in the Assertion and
+ * returns corresponding JBoss RoleGroup objects for insertion into the Subject.
+ * </p>
+ *
+ * <h3>Configuration</h3>
+ * <pre>{@code
+ * <application-policy name="saml-issue-token">
+ * <authentication>
+ * <login-module
code="org.picketlink.identity.federation.core.wstrust.auth.STSIssuingLoginModule"
flag="required">
+ * <module-option
name="configFile">/sts-client.properties</module-option>
+ * <module-option
name="password-stacking">useFirstPass</module-option>
+ * </login-module>
+ * </authentication>
+ * <mapping>
+ * <mapping-module
code="org.picketlink.identity.federation.bindings.jboss.auth.mapping.STSPrincipalMappingProvider"
type="principal"/>
+ * <mapping-module
code="org.picketlink.identity.federation.bindings.jboss.auth.mapping.STSGroupMappingProvider"
type="role">
+ * <module-option
name="token-role-attribute-name">role</module-option>
+ * </mapping-module>
+ * </mapping>
+ * </application-policy>
+ * }
+ * </pre>
+ *
+ * As demonstrated above, this mapping provider is typically configured for an STS Login
Module to extract user roles
+ * from the STS token and supply them for insertion into the JAAS Subject.
+ *
+ * This mapping provider looks for a multi-valued Attribute in the Assertion, where each
value is a user role.
+ * The name of this attribute defaults to {@code
SAML20TokenRoleAttributeProvider.DEFAULT_TOKEN_ROLE_ATTRIBUTE_NAME} but
+ * may be set to any value through the "token-role-attribute-name" module
option.
+ * <p/>
+ *
+ *
+ * @author <a href="mailto:Babak@redhat.com">Babak Mozaffari</a>
+ */
+public class STSGroupMappingProvider implements MappingProvider<RoleGroup>
+{
+ private Logger log = Logger.getLogger(STSGroupMappingProvider.class);
+
+ private MappingResult<RoleGroup> result;
+
+ private String tokenRoleAttributeName;
+
+ @Override
+ public void init(Map<String, Object> contextMap)
+ {
+ Object tokenRoleAttributeObject =
contextMap.get("token-role-attribute-name");
+ if (tokenRoleAttributeObject != null)
+ {
+ tokenRoleAttributeName = (String) tokenRoleAttributeObject;
+ }
+ else
+ {
+ tokenRoleAttributeName =
SAML20TokenRoleAttributeProvider.DEFAULT_TOKEN_ROLE_ATTRIBUTE_NAME;
+ }
+
+ //No initialization needed
+ if (log.isDebugEnabled())
+ {
+ log.debug("Initialized with " + contextMap);
+ }
+ }
+
+ @Override
+ public void performMapping(Map<String, Object> contextMap, RoleGroup Group)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("performMapping with map as " + contextMap);
+ }
+ if (contextMap == null)
+ {
+ throw new IllegalArgumentException(
+ "Empty context map. SAML Token must be provided in the context map to
extract a Principal");
+ }
+
+ Object tokenObject = contextMap.get(AbstractSTSLoginModule.SHARED_TOKEN);
+ if (!(tokenObject instanceof Element))
+ {
+ throw new IllegalArgumentException("Did not find a token " +
Element.class.getClass().getName() + " under "
+ + AbstractSTSLoginModule.SHARED_TOKEN + " in the map");
+ }
+
+ try
+ {
+ Element tokenElement = (Element) tokenObject;
+ AssertionType assertion = SAMLUtil.fromElement(tokenElement);
+
+ // check the assertion statements and look for role attributes.
+ AttributeStatementType attributeStatement =
this.getAttributeStatement(assertion);
+ if (attributeStatement != null)
+ {
+ RoleGroup rolesGroup = new
SimpleRoleGroup(SAML20TokenRoleAttributeProvider.JBOSS_ROLE_PRINCIPAL_NAME);
+ List<Object> attributeList =
attributeStatement.getAttributeOrEncryptedAttribute();
+ for (Object obj : attributeList)
+ {
+ if (obj instanceof AttributeType)
+ {
+ AttributeType attribute = (AttributeType) obj;
+ // if this is a role attribute, get its values and add them to the role
set.
+ if (tokenRoleAttributeName.equals(attribute.getName()))
+ {
+ for (Object value : attribute.getAttributeValue())
+ {
+ rolesGroup.addRole(new SimpleRole((String) value));
+ }
+ }
+ }
+ }
+ result.setMappedObject(rolesGroup);
+ if (log.isDebugEnabled())
+ {
+ log.debug("Mapped roles to " + rolesGroup);
+ }
+ }
+ }
+ catch (JAXBException e)
+ {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @Override
+ public void setMappingResult(MappingResult<RoleGroup> mappingResult)
+ {
+ this.result = mappingResult;
+ }
+
+ /**
+ * @see MappingProvider#supports(Class)
+ */
+ public boolean supports(Class<?> p)
+ {
+ if (RoleGroup.class.isAssignableFrom(p))
+ return true;
+
+ return false;
+ }
+
+ /**
+ * <p>
+ * Checks if the specified SAML assertion contains a {@code AttributeStatementType}
and returns this type when it
+ * is available.
+ * </p>
+ *
+ * @param assertion a reference to the {@code AssertionType} that may contain an
{@code AttributeStatementType}.
+ * @return the assertion's {@code AttributeStatementType}, or {@code null} if no
such type can be found in the SAML
+ * assertion.
+ */
+ private AttributeStatementType getAttributeStatement(AssertionType assertion)
+ {
+ List<StatementAbstractType> statementList =
assertion.getStatementOrAuthnStatementOrAuthzDecisionStatement();
+ if (statementList.size() != 0)
+ {
+ for (StatementAbstractType statement : statementList)
+ {
+ if (statement instanceof AttributeStatementType)
+ return (AttributeStatementType) statement;
+ }
+ }
+ return null;
+ }
+}
Added:
federation/trunk/picketlink-bindings-jboss/src/main/java/org/picketlink/identity/federation/bindings/jboss/auth/mapping/STSPrincipalMappingProvider.java
===================================================================
---
federation/trunk/picketlink-bindings-jboss/src/main/java/org/picketlink/identity/federation/bindings/jboss/auth/mapping/STSPrincipalMappingProvider.java
(rev 0)
+++
federation/trunk/picketlink-bindings-jboss/src/main/java/org/picketlink/identity/federation/bindings/jboss/auth/mapping/STSPrincipalMappingProvider.java 2010-06-11
19:25:03 UTC (rev 296)
@@ -0,0 +1,107 @@
+package org.picketlink.identity.federation.bindings.jboss.auth.mapping;
+
+import java.security.Principal;
+import java.util.Map;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.bind.JAXBException;
+
+import org.apache.log4j.Logger;
+import org.jboss.security.SimplePrincipal;
+import org.jboss.security.mapping.MappingResult;
+import org.jboss.security.mapping.providers.principal.AbstractPrincipalMappingProvider;
+import org.picketlink.identity.federation.core.wstrust.auth.AbstractSTSLoginModule;
+import org.picketlink.identity.federation.core.wstrust.plugins.saml.SAMLUtil;
+import org.picketlink.identity.federation.saml.v2.assertion.AssertionType;
+import org.picketlink.identity.federation.saml.v2.assertion.NameIDType;
+import org.picketlink.identity.federation.saml.v2.assertion.SubjectType;
+import org.w3c.dom.Element;
+
+/**
+ * <p>
+ * This mapping provider looks at the NameID in the Assertion and
+ * returns a corresponding JBoss Principal for insertion into the Subject.
+ * </p>
+ *
+ * <h3>Configuration</h3>
+ * <pre>{@code
+ * <application-policy name="saml-issue-token">
+ * <authentication>
+ * <login-module
code="org.picketlink.identity.federation.core.wstrust.auth.STSIssuingLoginModule"
flag="required">
+ * <module-option
name="configFile">/sts-client.properties</module-option>
+ * <module-option
name="password-stacking">useFirstPass</module-option>
+ * </login-module>
+ * </authentication>
+ * <mapping>
+ * <mapping-module
code="org.picketlink.identity.federation.bindings.jboss.auth.mapping.STSPrincipalMappingProvider"
type="principal"/>
+ * <mapping-module
code="org.picketlink.identity.federation.bindings.jboss.auth.mapping.STSGroupMappingProvider"
type="role"/>
+ * </mapping>
+ * </application-policy>
+ * }
+ * </pre>
+ *
+ * @author <a href="mailto:Babak@redhat.com">Babak Mozaffari</a>
+ */
+public class STSPrincipalMappingProvider extends AbstractPrincipalMappingProvider
+{
+ private Logger log = Logger.getLogger(STSPrincipalMappingProvider.class);
+
+ private MappingResult<Principal> result;
+
+ @Override
+ public void init(Map<String, Object> contextMap)
+ {
+ //No initialization needed
+ }
+
+ @Override
+ public void performMapping(Map<String, Object> contextMap, Principal principal)
+ {
+ if (contextMap == null)
+ {
+ throw new IllegalArgumentException(
+ "Empty context map. SAML Token must be provided in the context map to
extract a Principal");
+ }
+
+ Object tokenObject = contextMap.get(AbstractSTSLoginModule.SHARED_TOKEN);
+ if (!(tokenObject instanceof Element))
+ {
+ throw new IllegalArgumentException("Did not find a token " +
Element.class.getClass().getName() + " under "
+ + AbstractSTSLoginModule.SHARED_TOKEN + " in the map");
+ }
+
+ try
+ {
+ Element tokenElement = (Element) tokenObject;
+ AssertionType assertion = SAMLUtil.fromElement(tokenElement);
+ SubjectType subject = assertion.getSubject();
+ if (subject != null)
+ {
+ for (JAXBElement<?> element : subject.getContent())
+ {
+ if (element.getDeclaredType().equals(NameIDType.class))
+ {
+ NameIDType nameID = (NameIDType) element.getValue();
+ Principal mappedPrincipal = new SimplePrincipal(nameID.getValue());
+ result.setMappedObject(mappedPrincipal);
+ if (log.isDebugEnabled())
+ {
+ log.debug("Mapped principal to " + mappedPrincipal);
+ }
+ return;
+ }
+ }
+ }
+ }
+ catch (JAXBException e)
+ {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
+ @Override
+ public void setMappingResult(MappingResult<Principal> mappingResult)
+ {
+ this.result = mappingResult;
+ }
+}
Added:
federation/trunk/picketlink-bindings-jboss/src/test/java/org/picketlink/identity/federation/bindings/jboss/auth/STSMappingProviderUnitTestCase.java
===================================================================
---
federation/trunk/picketlink-bindings-jboss/src/test/java/org/picketlink/identity/federation/bindings/jboss/auth/STSMappingProviderUnitTestCase.java
(rev 0)
+++
federation/trunk/picketlink-bindings-jboss/src/test/java/org/picketlink/identity/federation/bindings/jboss/auth/STSMappingProviderUnitTestCase.java 2010-06-11
19:25:03 UTC (rev 296)
@@ -0,0 +1,138 @@
+/*
+ * 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.bindings.jboss.auth;
+
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.bind.JAXBElement;
+import javax.xml.namespace.QName;
+
+import junit.framework.TestCase;
+
+import org.jboss.security.identity.RoleGroup;
+import org.jboss.security.mapping.MappingProvider;
+import org.jboss.security.mapping.MappingResult;
+import
org.picketlink.identity.federation.bindings.jboss.auth.mapping.STSGroupMappingProvider;
+import
org.picketlink.identity.federation.bindings.jboss.auth.mapping.STSPrincipalMappingProvider;
+import org.picketlink.identity.federation.core.wstrust.WSTrustConstants;
+import org.picketlink.identity.federation.core.wstrust.auth.AbstractSTSLoginModule;
+import org.picketlink.identity.federation.core.wstrust.plugins.saml.SAMLUtil;
+import org.picketlink.identity.federation.saml.v2.assertion.AssertionType;
+import org.picketlink.identity.federation.saml.v2.assertion.AttributeStatementType;
+import org.picketlink.identity.federation.saml.v2.assertion.AttributeType;
+import org.picketlink.identity.federation.saml.v2.assertion.NameIDType;
+import org.picketlink.identity.federation.saml.v2.assertion.SubjectType;
+import org.w3c.dom.Element;
+
+/**
+ * <p>
+ * This {@code TestCase} tests the functionalities of {@code STSPrincipalMappingProvider}
and {@code STSGroupMappingProvider}.
+ * </p>
+ *
+ * @author <a href="mailto:Babak@redhat.com">Babak Mozaffari</a>
+ */
+public class STSMappingProviderUnitTestCase extends TestCase
+{
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ }
+
+ /**
+ * <p>
+ * Tests that {@code STSGroupMappingProvider} correctly maps and returns a {@code
RoleGroup}
+ * </p>
+ *
+ * @throws Exception if an error occurs while running the test.
+ */
+ public void testSTSGroupMappingProvider() throws Exception
+ {
+ String roleAttributeName = "roleAttributeName";
+ String role1 = "userRole1";
+ String role2 = "userRole2";
+
+ AssertionType assertion = new AssertionType();
+ AttributeStatementType attributeStatementType = new AttributeStatementType();
+
assertion.getStatementOrAuthnStatementOrAuthzDecisionStatement().add(attributeStatementType);
+ AttributeType attributeType = new AttributeType();
+ attributeStatementType.getAttributeOrEncryptedAttribute().add(attributeType);
+ attributeType.setName(roleAttributeName);
+ attributeType.getAttributeValue().add(role1);
+ attributeType.getAttributeValue().add(role2);
+
+ MappingResult<RoleGroup> mappingResult = new
MappingResult<RoleGroup>();
+ Map<String, Object> contextMap = new HashMap<String, Object>();
+ contextMap.put("token-role-attribute-name", roleAttributeName);
+ contextMap.put(AbstractSTSLoginModule.SHARED_TOKEN,
SAMLUtil.toElement(assertion));
+
+ MappingProvider<RoleGroup> mappingProvider = new STSGroupMappingProvider();
+ mappingProvider.init(contextMap);
+ mappingProvider.setMappingResult(mappingResult);
+ mappingProvider.performMapping(contextMap, null);
+
+ RoleGroup roleGroup = mappingResult.getMappedObject();
+ assertNotNull("Unexpected null mapped role", roleGroup);
+ assertEquals("RoleGroup name has unexpected value",
SAML20TokenRoleAttributeProvider.JBOSS_ROLE_PRINCIPAL_NAME, roleGroup.getRoleName());
+ assertEquals("RoleGroup has unexpected first role", role1,
roleGroup.getRoles().get(0).getRoleName());
+ assertEquals("RoleGroup has unexpected second role", role2,
roleGroup.getRoles().get(1).getRoleName());
+ }
+
+ /**
+ * <p>
+ * Tests that {@code STSPrincipalMappingProvider} correctly maps and returns a {@code
Principal}
+ * </p>
+ *
+ * @throws Exception if an error occurs while running the test.
+ */
+ public void testSTSPrincipalMappingProvider() throws Exception
+ {
+ String userId = "babak";
+
+ AssertionType assertion = new AssertionType();
+ SubjectType subjectType = new SubjectType();
+ assertion.setSubject(subjectType);
+ QName name = new QName(WSTrustConstants.SAML2_ASSERTION_NS, "NameID");
+ Class<NameIDType> declaredType = NameIDType.class;
+ NameIDType nameIDType = new NameIDType();
+ nameIDType.setValue(userId);
+ JAXBElement<NameIDType> jaxbElement = new JAXBElement<NameIDType>(name,
declaredType, JAXBElement.GlobalScope.class, nameIDType);
+ subjectType.getContent().add(jaxbElement);
+
+ MappingResult<Principal> mappingResult = new
MappingResult<Principal>();
+ Map<String, Object> contextMap = new HashMap<String, Object>();
+ Element assertionElement = SAMLUtil.toElement(assertion);
+ contextMap.put(AbstractSTSLoginModule.SHARED_TOKEN, assertionElement);
+
+ MappingProvider<Principal> mappingProvider = new
STSPrincipalMappingProvider();
+ mappingProvider.init(contextMap);
+ mappingProvider.setMappingResult(mappingResult);
+ mappingProvider.performMapping(contextMap, null);
+
+ Principal principal = mappingResult.getMappedObject();
+ assertNotNull("Unexpected null mapped principal", principal);
+ assertEquals("Principal has unexpected value", userId,
principal.getName());
+ }
+}