Reworked name mapping configuration:
authorwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Tue, 10 May 2005 21:52:15 +0000 (21:52 +0000)
committerwassa <wassa@ab3bd59b-922f-494d-bb5f-6f0a3c29deca>
Tue, 10 May 2005 21:52:15 +0000 (21:52 +0000)
Negotiate proper format for SSO based on metadata & relying party configuration.
Removed "HS" naming.
Attribute queries are restricted to mappings configured for the active relying party.

git-svn-id: https://subversion.switch.ch/svn/shibboleth/java-idp/trunk@1465 ab3bd59b-922f-494d-bb5f-6f0a3c29deca

src/edu/internet2/middleware/shibboleth/common/NameMapper.java
src/edu/internet2/middleware/shibboleth/common/RelyingParty.java
src/edu/internet2/middleware/shibboleth/common/ServiceProviderMapper.java
src/edu/internet2/middleware/shibboleth/idp/provider/E_AuthSSOHandler.java
src/edu/internet2/middleware/shibboleth/idp/provider/SAMLv1_AttributeQueryHandler.java
src/edu/internet2/middleware/shibboleth/idp/provider/SSOHandler.java
src/edu/internet2/middleware/shibboleth/idp/provider/ShibbolethV1SSOHandler.java
src/schemas/shibboleth-idpconfig-1.0.xsd
tests/edu/internet2/middleware/shibboleth/common/provider/NameMapperTests.java

index 062409d..1e28f10 100644 (file)
@@ -285,7 +285,7 @@ public class NameMapper {
         * @throws NameIdentifierMappingException
         *             If the <code>NameMapper</code> encounters an internal error
         */
-       public SAMLNameIdentifier getNameIdentifierName(String id, LocalPrincipal principal, ServiceProvider sProv,
+       public SAMLNameIdentifier getNameIdentifier(String id, LocalPrincipal principal, ServiceProvider sProv,
                        IdentityProvider idProv) throws NameIdentifierMappingException {
 
                NameIdentifierMapping mapping = getNameIdentifierMappingById(id);
index e76e0ee..0859cf4 100644 (file)
@@ -52,12 +52,13 @@ public interface RelyingParty extends ServiceProvider {
        public IdentityProvider getIdentityProvider();
 
        /**
-        * Returns the id of the name format that should be used in authentication assertions issued to this
-        * {@link RelyingParty}.
+        * Returns an array of identifiers for looking up the name mappings to be used when responding to queries from this
+        * {@link RelyingParty}. The array is ordered by the preference that should be given to use of the given name
+        * mappings.
         * 
-        * @return the id for the format
+        * @return the ids of the mappers
         */
-       public String getHSNameFormatId();
+       public String[] getNameMapperIds();
 
        /**
         * Returns a boolean indication of whether this {@link RelyingParty}is running &lt;= Shibboleth v1.1. Used to
index c00f87c..b7a28a6 100644 (file)
@@ -29,7 +29,9 @@ import java.net.MalformedURLException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import org.apache.log4j.Logger;
@@ -38,8 +40,8 @@ import org.w3c.dom.NodeList;
 
 import edu.internet2.middleware.shibboleth.idp.IdPConfig;
 import edu.internet2.middleware.shibboleth.metadata.EntitiesDescriptor;
-import edu.internet2.middleware.shibboleth.metadata.Metadata;
 import edu.internet2.middleware.shibboleth.metadata.EntityDescriptor;
+import edu.internet2.middleware.shibboleth.metadata.Metadata;
 
 /**
  * Class for determining the effective relying party from the unique id of the service provider. Checks first for an
@@ -172,8 +174,7 @@ public class ServiceProviderMapper {
        public RelyingParty getLegacyRelyingParty() {
 
                RelyingParty relyingParty = getDefaultRelyingParty();
-               log.info("Request is from legacy shib SP.  Selecting default Relying Party: (" + relyingParty.getName()
-                               + ").");
+               log.info("Request is from legacy shib SP.  Selecting default Relying Party: (" + relyingParty.getName() + ").");
                return new LegacyWrapper((RelyingParty) relyingParty);
 
        }
@@ -219,7 +220,7 @@ public class ServiceProviderMapper {
                private String overridenIdPProviderId;
                private URL overridenAAUrl;
                private URI overridenDefaultAuthMethod;
-               private String hsNameFormatId;
+               private List mappingIds = new ArrayList();
                private IdPConfig configuration;
                private boolean overridenPassThruErrors = false;
                private boolean passThruIsOverriden = false;
@@ -341,34 +342,36 @@ public class ServiceProviderMapper {
                                log.debug("Preferred artifact type: (" + preferredArtifactType + ").");
                        }
 
-                       // Load and verify the name format that the HS should use in
+                       // Load and verify the name mappings that should be used in
                        // assertions for this RelyingParty
-                       NodeList hsNameFormats = ((Element) partyConfig).getElementsByTagNameNS(IdPConfig.configNameSpace,
-                                       "HSNameFormat");
+
+                       NodeList nameIDs = ((Element) partyConfig).getElementsByTagNameNS(IdPConfig.configNameSpace, "NameID");
                        // If no specification. Make sure we have a default mapping
-                       if (hsNameFormats.getLength() < 1) {
+                       if (nameIDs.getLength() < 1) {
                                if (nameMapper.getNameIdentifierMappingById(null) == null) {
-                                       log.error("Relying Party HS Name Format not set.  Add a <HSNameFormat> element to <RelyingParty>.");
+                                       log.error("Relying Party NameId configuration not set.  Add a <NameID> element to <RelyingParty>.");
                                        throw new ServiceProviderMapperException("Required configuration not specified.");
                                }
 
                        } else {
+
                                // We do have a specification, so make sure it points to a
                                // valid Name Mapping
-                               if (hsNameFormats.getLength() > 1) {
-                                       log.warn("Found multiple HSNameFormat specifications for Relying Party (" + name
-                                                       + ").  Ignoring all but the first.");
-                               }
 
-                               hsNameFormatId = ((Element) hsNameFormats.item(0)).getAttribute("nameMapping");
-                               if (hsNameFormatId == null || hsNameFormatId.equals("")) {
-                                       log.error("HS Name Format mapping not set.  Add a (nameMapping) attribute to <HSNameFormat>.");
-                                       throw new ServiceProviderMapperException("Required configuration not specified.");
-                               }
+                               for (int i = 0; i < nameIDs.getLength(); i++) {
 
-                               if (nameMapper.getNameIdentifierMappingById(hsNameFormatId) == null) {
-                                       log.error("Relying Party HS Name Format refers to a name mapping that is not loaded.");
-                                       throw new ServiceProviderMapperException("Required configuration not specified.");
+                                       String mappingId = ((Element) nameIDs.item(i)).getAttribute("nameMapping");
+                                       if (mappingId == null || mappingId.equals("")) {
+                                               log.error("Name mapping not set.  Add a (nameMapping) attribute to <NameID>.");
+                                               throw new ServiceProviderMapperException("Required configuration not specified.");
+                                       }
+
+                                       if (nameMapper.getNameIdentifierMappingById(mappingId) == null) {
+                                               log.error("Relying Party NameID refers to a name mapping that is not loaded.");
+                                               throw new ServiceProviderMapperException("Required configuration not specified.");
+                                       }
+
+                                       mappingIds.add(mappingId);
                                }
                        }
 
@@ -415,9 +418,9 @@ public class ServiceProviderMapper {
                        return false;
                }
 
-               public String getHSNameFormatId() {
+               public String[] getNameMapperIds() {
 
-                       return hsNameFormatId;
+                       return (String[]) mappingIds.toArray(new String[0]);
                }
 
                public URI getDefaultAuthMethod() {
@@ -553,9 +556,9 @@ public class ServiceProviderMapper {
                        return providerId;
                }
 
-               public String getHSNameFormatId() {
+               public String[] getNameMapperIds() {
 
-                       return wrapped.getHSNameFormatId();
+                       return wrapped.getNameMapperIds();
                }
 
                public URL getAAUrl() {
@@ -640,9 +643,9 @@ public class ServiceProviderMapper {
                        return providerId;
                }
 
-               public String getHSNameFormatId() {
+               public String[] getNameMapperIds() {
 
-                       return wrapped.getHSNameFormatId();
+                       return wrapped.getNameMapperIds();
                }
 
                public boolean isLegacyProvider() {
@@ -718,9 +721,9 @@ public class ServiceProviderMapper {
                        return true;
                }
 
-               public String getHSNameFormatId() {
+               public String[] getNameMapperIds() {
 
-                       return ((RelyingParty) wrapped).getHSNameFormatId();
+                       return ((RelyingParty) wrapped).getNameMapperIds();
                }
 
                public URL getAAUrl() {
@@ -746,9 +749,9 @@ public class ServiceProviderMapper {
                        super(wrapped, null);
                }
 
-               public String getHSNameFormatId() {
+               public String[] getNameMapperIds() {
 
-                       return ((RelyingParty) wrapped).getHSNameFormatId();
+                       return ((RelyingParty) wrapped).getNameMapperIds();
                }
 
                public URL getAAUrl() {
index 3819ec6..cdf660d 100644 (file)
@@ -214,8 +214,7 @@ public class E_AuthSSOHandler extends SSOHandler implements IdPProtocolHandler {
                // Create SAML Name Identifier & Subject
                SAMLNameIdentifier nameId;
                try {
-                       nameId = support.getNameMapper().getNameIdentifierName(relyingParty.getHSNameFormatId(), principal,
-                                       relyingParty, relyingParty.getIdentityProvider());
+                       nameId = getNameIdentifier(support.getNameMapper(), principal, relyingParty, entity);
                        if (!nameId.getFormat().equals(E_AUTH_NAMEID)) {
                                log.error("SAML Name Identifier format is inappropriate for use with E-Authentication provider.  Was ("
                                                + nameId.getFormat() + ").  Expected (" + E_AUTH_NAMEID + ").");
index 442b3c4..9960df2 100644 (file)
@@ -50,6 +50,7 @@ import org.opensaml.SAMLAttributeStatement;
 import org.opensaml.SAMLAudienceRestrictionCondition;
 import org.opensaml.SAMLCondition;
 import org.opensaml.SAMLException;
+import org.opensaml.SAMLNameIdentifier;
 import org.opensaml.SAMLRequest;
 import org.opensaml.SAMLResponse;
 import org.opensaml.SAMLStatement;
@@ -58,6 +59,7 @@ import org.w3c.dom.Element;
 
 import edu.internet2.middleware.shibboleth.aa.AAException;
 import edu.internet2.middleware.shibboleth.common.InvalidNameIdentifierException;
+import edu.internet2.middleware.shibboleth.common.NameIdentifierMapping;
 import edu.internet2.middleware.shibboleth.common.NameIdentifierMappingException;
 import edu.internet2.middleware.shibboleth.common.RelyingParty;
 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
@@ -202,13 +204,27 @@ public class SAMLv1_AttributeQueryHandler extends BaseServiceHandler implements
                }
 
                // Map Subject to local principal
-               Principal principal;
+               Principal principal = null;
                try {
-                       principal = support.getNameMapper().getPrincipal(attributeQuery.getSubject().getNameIdentifier(),
-                                       relyingParty, relyingParty.getIdentityProvider());
+                       SAMLNameIdentifier nameId = attributeQuery.getSubject().getNameIdentifier();
+                       log.debug("Name Identifier format: (" + nameId.getFormat() + ").");
+                       NameIdentifierMapping mapping = null;
+                       try {
+                               mapping = support.getNameMapper().getNameIdentifierMapping(new URI(nameId.getFormat()));
+                       } catch (URISyntaxException e) {
+                               log.error("Invalid Name Identifier format.");
+                       }
+                       if (mapping == null) { throw new NameIdentifierMappingException("Name Identifier format not registered."); }
+
+                       // Don't honor the request if the active relying party configuration does not contain a mapping with the
+                       // name identifier format from the request
+                       if (!Arrays.asList(relyingParty.getNameMapperIds()).contains(mapping.getId())) { throw new NameIdentifierMappingException(
+                                       "Name Identifier format not valid for this relying party."); }
 
+                       principal = mapping.getPrincipal(nameId, relyingParty, relyingParty.getIdentityProvider());
                        log.info("Request is for principal (" + principal.getName() + ").");
 
+                       // Get attributes from resolver
                        SAMLAttribute[] attrs;
                        Iterator requestedAttrsIterator = attributeQuery.getDesignators();
                        if (requestedAttrsIterator.hasNext()) {
index 55cd5f6..909fd93 100644 (file)
@@ -28,16 +28,25 @@ package edu.internet2.middleware.shibboleth.idp.provider;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.Iterator;
 
 import javax.servlet.http.HttpServletRequest;
 
 import org.apache.log4j.Logger;
 import org.opensaml.SAMLException;
+import org.opensaml.SAMLNameIdentifier;
 import org.w3c.dom.Element;
 
+import edu.internet2.middleware.shibboleth.common.LocalPrincipal;
+import edu.internet2.middleware.shibboleth.common.NameIdentifierMapping;
+import edu.internet2.middleware.shibboleth.common.NameIdentifierMappingException;
+import edu.internet2.middleware.shibboleth.common.NameMapper;
+import edu.internet2.middleware.shibboleth.common.RelyingParty;
 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
 import edu.internet2.middleware.shibboleth.idp.IdPProtocolHandler;
 import edu.internet2.middleware.shibboleth.idp.InvalidClientDataException;
+import edu.internet2.middleware.shibboleth.metadata.EntityDescriptor;
+import edu.internet2.middleware.shibboleth.metadata.SPSSODescriptor;
 
 /**
  * @author Walter Hoehn
@@ -77,4 +86,56 @@ public abstract class SSOHandler extends BaseHandler implements IdPProtocolHandl
                        return new Date(System.currentTimeMillis());
                }
        }
+
+       /**
+        * Constructs a SAML Name Identifier of a given principal that is most appropriate to the relying party.
+        * 
+        * @param mapper
+        *            name mapping facility
+        * @param principal
+        *            the principal represented by the name identifier
+        * @param relyingParty
+        *            the party that will consume the name identifier
+        * @param descriptor
+        *            metadata descriptor for the party that will consume the name identifier
+        * @return the SAML Name identifier
+        * @throws NameIdentifierMappingException
+        *             if a name identifier could not be created
+        */
+       protected SAMLNameIdentifier getNameIdentifier(NameMapper mapper, LocalPrincipal principal,
+                       RelyingParty relyingParty, EntityDescriptor descriptor) throws NameIdentifierMappingException {
+
+               String[] availableMappings = relyingParty.getNameMapperIds();
+
+               SPSSODescriptor role = descriptor.getSPSSODescriptor("urn:oasis:names:tc:SAML:1.1:protocol");
+
+               // If we have preferred Name Identifier formats from the metadata, see if the we can find one that is configured
+               // for this relying party
+               if (role != null) {
+                       Iterator spPreferredFormats = role.getNameIDFormats();
+                       while (spPreferredFormats.hasNext()) {
+
+                               String preferredFormat = (String) spPreferredFormats.next();
+                               for (int i = 0; availableMappings != null && i < availableMappings.length; i++) {
+                                       NameIdentifierMapping mapping = mapper.getNameIdentifierMappingById(availableMappings[i]);
+                                       if (mapping != null && preferredFormat.equals(mapping.getNameIdentifierFormat().toString())) {
+                                               log.debug("Found a supported name identifier format that "
+                                                               + "matches the metadata for the relying party: ("
+                                                               + mapping.getNameIdentifierFormat().toString() + ").");
+                                               return mapping.getNameIdentifier(principal, relyingParty, relyingParty.getIdentityProvider());
+                                       }
+                               }
+                       }
+               }
+
+               // If we didn't find any matches, then just use the default for the relying party
+               String defaultNameMapping = null;
+               if (availableMappings != null && availableMappings.length > 0) {
+                       defaultNameMapping = availableMappings[0];
+               }
+               SAMLNameIdentifier nameId = mapper.getNameIdentifier(defaultNameMapping, principal, relyingParty, relyingParty
+                               .getIdentityProvider());
+               log.debug("Using the default name identifier format for this relying party: (" + nameId.getFormat());
+               return nameId;
+       }
 }
\ No newline at end of file
index eddcf73..71fe3ed 100644 (file)
@@ -161,8 +161,7 @@ public class ShibbolethV1SSOHandler extends SSOHandler implements IdPProtocolHan
                        // Create SAML Name Identifier & Subject
                        SAMLNameIdentifier nameId;
                        try {
-                               nameId = support.getNameMapper().getNameIdentifierName(relyingParty.getHSNameFormatId(), principal,
-                                               relyingParty, relyingParty.getIdentityProvider());
+                               nameId = getNameIdentifier(support.getNameMapper(), principal, relyingParty, descriptor);
                        } catch (NameIdentifierMappingException e) {
                                log.error("Error converting principal to SAML Name Identifier: " + e);
                                throw new SAMLException("Error converting principal to SAML Name Identifier.", e);
index c087234..cdfdd84 100644 (file)
@@ -44,8 +44,8 @@
                                        <xs:sequence>
                                                <xs:element name="RelyingParty" maxOccurs="unbounded">
                                                        <xs:complexType>
-                                                               <xs:sequence minOccurs="0">
-                                                                       <xs:element name="HSNameFormat">
+                                                               <xs:sequence minOccurs="0" maxOccurs="unbounded">
+                                                                       <xs:element name="NameID">
                                                                                <xs:complexType>
                                                                                        <xs:attribute name="nameMapping" type="xs:string" use="required"/>
                                                                                </xs:complexType>
index b91f8cf..31e93c7 100644 (file)
@@ -101,7 +101,7 @@ public class NameMapperTests extends TestCase {
                        parser.parse(new InputSource(new StringReader(rawConfig)));
                        nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
 
-                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifierName("cryptotest", new LocalPrincipal(
+                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifier("cryptotest", new LocalPrincipal(
                                        "testprincipal"), new BasicServiceProvider(), new BasicIdentityProvider("urn-x:testid"));
 
                        Principal principal = nameMapper.getPrincipal(nameId, new BasicServiceProvider(),
@@ -139,7 +139,7 @@ public class NameMapperTests extends TestCase {
                        parser.parse(new InputSource(new StringReader(rawConfig)));
                        nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
 
-                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifierName("cryptotest", new LocalPrincipal(
+                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifier("cryptotest", new LocalPrincipal(
                                        "testprincipal"), new BasicServiceProvider(), new BasicIdentityProvider("urn-x:testid"));
 
                        log.debug("Waiting 11 seconds for the handle to expire.");
@@ -186,7 +186,7 @@ public class NameMapperTests extends TestCase {
                        parser.parse(new InputSource(new StringReader(rawConfig)));
                        nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
 
-                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifierName("cryptotest", new LocalPrincipal(
+                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifier("cryptotest", new LocalPrincipal(
                                        "testprincipal"), new BasicServiceProvider(), new BasicIdentityProvider("urn-x:testid"));
 
                        Principal principal = nameMapper.getPrincipal(nameId, new BasicServiceProvider(),
@@ -224,7 +224,7 @@ public class NameMapperTests extends TestCase {
                        parser.parse(new InputSource(new StringReader(rawConfig)));
                        nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
 
-                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifierName("cryptotest", new LocalPrincipal(
+                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifier("cryptotest", new LocalPrincipal(
                                        "testprincipal"), new BasicServiceProvider(), new BasicIdentityProvider("urn-x:good"));
 
                        Principal principal = nameMapper.getPrincipal(nameId, new BasicServiceProvider(),
@@ -250,7 +250,7 @@ public class NameMapperTests extends TestCase {
 
                        NameMapper nameMapper = new NameMapper();
 
-                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifierName(null, new LocalPrincipal("testprincipal"),
+                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifier(null, new LocalPrincipal("testprincipal"),
                                        new BasicServiceProvider(), new BasicIdentityProvider("urn-x:testid"));
 
                        Principal principal = nameMapper.getPrincipal(nameId, new BasicServiceProvider(),
@@ -286,7 +286,7 @@ public class NameMapperTests extends TestCase {
                        parser.parse(new InputSource(new StringReader(rawConfig)));
                        nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
 
-                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifierName(null, new LocalPrincipal("testprincipal"),
+                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifier(null, new LocalPrincipal("testprincipal"),
                                        new BasicServiceProvider(), new BasicIdentityProvider("urn-x:testid"));
 
                        Principal principal = nameMapper.getPrincipal(nameId, new BasicServiceProvider(),
@@ -341,7 +341,7 @@ public class NameMapperTests extends TestCase {
 
                        nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
 
-                       nameMapper.getNameIdentifierName(null, new LocalPrincipal("testprincipal"), new BasicServiceProvider(),
+                       nameMapper.getNameIdentifier(null, new LocalPrincipal("testprincipal"), new BasicServiceProvider(),
                                        new BasicIdentityProvider("urn-x:testid"));
 
                        fail("HSNameMapper defaulted to incorrect name mapping.");
@@ -371,7 +371,7 @@ public class NameMapperTests extends TestCase {
                        parser.parse(new InputSource(new StringReader(rawConfig)));
                        nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
 
-                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifierName("memorytest", new LocalPrincipal(
+                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifier("memorytest", new LocalPrincipal(
                                        "testprincipal"), new BasicServiceProvider(), new BasicIdentityProvider("urn-x:testid"));
 
                        Principal principal = nameMapper.getPrincipal(nameId, new BasicServiceProvider(),
@@ -404,7 +404,7 @@ public class NameMapperTests extends TestCase {
                        parser.parse(new InputSource(new StringReader(rawConfig)));
                        nameMapper.addNameMapping(parser.getDocument().getDocumentElement());
 
-                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifierName("memory", new LocalPrincipal("testprincipal"),
+                       SAMLNameIdentifier nameId = nameMapper.getNameIdentifier("memory", new LocalPrincipal("testprincipal"),
                                        new BasicServiceProvider(), new BasicIdentityProvider("urn-x:good"));
 
                        Principal principal = nameMapper.getPrincipal(nameId, new BasicServiceProvider(),