Make sure groups have names.
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / common / ServiceProviderMapper.java
index dfcc0a4..264f05b 100644 (file)
@@ -1,26 +1,17 @@
 /*
- * The Shibboleth License, Version 1. Copyright (c) 2002 University Corporation for Advanced Internet Development, Inc.
- * All rights reserved Redistribution and use in source and binary forms, with or without modification, are permitted
- * provided that the following conditions are met: Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials
- * provided with the distribution, if any, must include the following acknowledgment: "This product includes software
- * developed by the University Corporation for Advanced Internet Development <http://www.ucaid.edu> Internet2 Project.
- * Alternately, this acknowledegement may appear in the software itself, if and wherever such third-party
- * acknowledgments normally appear. Neither the name of Shibboleth nor the names of its contributors, nor Internet2, nor
- * the University Corporation for Advanced Internet Development, Inc., nor UCAID may be used to endorse or promote
- * products derived from this software without specific prior written permission. For written permission, please contact
- * shibboleth@shibboleth.org Products derived from this software may not be called Shibboleth, Internet2, UCAID, or the
- * University Corporation for Advanced Internet Development, nor may Shibboleth appear in their name, without prior
- * written permission of the University Corporation for Advanced Internet Development. THIS SOFTWARE IS PROVIDED BY THE
- * COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND WITH ALL FAULTS. ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT ARE
- * DISCLAIMED AND THE ENTIRE RISK OF SATISFACTORY QUALITY, PERFORMANCE, ACCURACY, AND EFFORT IS WITH LICENSEE. IN NO
- * EVENT SHALL THE COPYRIGHT OWNER, CONTRIBUTORS OR THE UNIVERSITY CORPORATION FOR ADVANCED INTERNET DEVELOPMENT, INC.
- * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
- * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
- * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
 package edu.internet2.middleware.shibboleth.common;
@@ -29,7 +20,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 +31,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
@@ -104,49 +97,52 @@ public class ServiceProviderMapper {
                }
        }
 
-       protected RelyingParty getRelyingPartyImpl(String providerIdFromTarget) {
+       protected RelyingParty getRelyingPartyImpl(String providerIdFromSP) {
 
                // Null request, send the default
-               if (providerIdFromTarget == null) {
+               if (providerIdFromSP == null) {
                        RelyingParty relyingParty = getDefaultRelyingParty();
                        log.info("Using default Relying Party: (" + relyingParty.getName() + ").");
-                       return new UnknownProviderWrapper(relyingParty, providerIdFromTarget);
+                       return new UnknownProviderWrapper(relyingParty, providerIdFromSP);
                }
 
                // Look for a configuration for the specific relying party
-               if (relyingParties.containsKey(providerIdFromTarget)) {
-                       log.info("Found Relying Party for (" + providerIdFromTarget + ").");
-                       return (RelyingParty) relyingParties.get(providerIdFromTarget);
+               if (relyingParties.containsKey(providerIdFromSP)) {
+                       log.info("Found Relying Party for (" + providerIdFromSP + ").");
+                       return (RelyingParty) relyingParties.get(providerIdFromSP);
                }
 
                // Next, check to see if the relying party is in any groups
-               RelyingParty groupParty = findRelyingPartyByGroup(providerIdFromTarget);
+               RelyingParty groupParty = findRelyingPartyByGroup(providerIdFromSP);
                if (groupParty != null) {
                        log.info("Provider is a member of Relying Party (" + groupParty.getName() + ").");
-                       return new RelyingPartyGroupWrapper(groupParty, providerIdFromTarget);
+                       return new RelyingPartyGroupWrapper(groupParty, providerIdFromSP);
                }
 
                // OK, we can't find it... just send the default
                RelyingParty relyingParty = getDefaultRelyingParty();
-               log.info("Could not locate Relying Party configuration for (" + providerIdFromTarget
+               log.info("Could not locate Relying Party configuration for (" + providerIdFromSP
                                + ").  Using default Relying Party: (" + relyingParty.getName() + ").");
-               return new UnknownProviderWrapper(relyingParty, providerIdFromTarget);
+               return new UnknownProviderWrapper(relyingParty, providerIdFromSP);
        }
 
-       private RelyingParty findRelyingPartyByGroup(String providerIdFromTarget) {
+       private RelyingParty findRelyingPartyByGroup(String providerIdFromSP) {
 
                if (metaData == null) { return null; }
 
-               EntityDescriptor provider = metaData.lookup(providerIdFromTarget);
+               EntityDescriptor provider = metaData.lookup(providerIdFromSP);
                if (provider != null) {
                        EntitiesDescriptor parent = provider.getEntitiesDescriptor();
                        while (parent != null) {
-                               if (relyingParties.containsKey(parent.getName())) {
-                                       log.info("Found matching Relying Party for group (" + parent.getName() + ").");
-                                       return (RelyingParty) relyingParties.get(parent.getName());
-                               } else {
-                                       log.debug("Provider is a member of group (" + parent.getName()
-                                                       + "), but no matching Relying Party was found.");
+                               if (parent.getName() != null) {
+                                       if (relyingParties.containsKey(parent.getName())) {
+                                               log.info("Found matching Relying Party for group (" + parent.getName() + ").");
+                                               return (RelyingParty) relyingParties.get(parent.getName());
+                                       }
+                                       else {
+                                               log.debug("Provider is a member of group (" + parent.getName()
+                                                               + "), but no matching Relying Party was found.");
+                                       }
                                }
                                parent = parent.getEntitiesDescriptor();
                        }
@@ -172,8 +168,7 @@ public class ServiceProviderMapper {
        public RelyingParty getLegacyRelyingParty() {
 
                RelyingParty relyingParty = getDefaultRelyingParty();
-               log.info("Request is from legacy shib target.  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);
 
        }
@@ -181,15 +176,15 @@ public class ServiceProviderMapper {
        /**
         * Returns the appropriate relying party for the supplied service provider id.
         */
-       public RelyingParty getRelyingParty(String providerIdFromTarget) {
+       public RelyingParty getRelyingParty(String providerIdFromSP) {
 
-               if (providerIdFromTarget == null || providerIdFromTarget.equals("")) {
+               if (providerIdFromSP == null || providerIdFromSP.equals("")) {
                        RelyingParty relyingParty = getDefaultRelyingParty();
                        log.info("Selecting default Relying Party: (" + relyingParty.getName() + ").");
                        return new NoMetadataWrapper((RelyingParty) relyingParty);
                }
 
-               return (RelyingParty) getRelyingPartyImpl(providerIdFromTarget);
+               return (RelyingParty) getRelyingPartyImpl(providerIdFromSP);
        }
 
        private void addRelyingParty(Element e) throws ServiceProviderMapperException {
@@ -219,7 +214,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 +336,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 +412,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 +550,9 @@ public class ServiceProviderMapper {
                        return providerId;
                }
 
-               public String getHSNameFormatId() {
+               public String[] getNameMapperIds() {
 
-                       return wrapped.getHSNameFormatId();
+                       return wrapped.getNameMapperIds();
                }
 
                public URL getAAUrl() {
@@ -640,9 +637,9 @@ public class ServiceProviderMapper {
                        return providerId;
                }
 
-               public String getHSNameFormatId() {
+               public String[] getNameMapperIds() {
 
-                       return wrapped.getHSNameFormatId();
+                       return wrapped.getNameMapperIds();
                }
 
                public boolean isLegacyProvider() {
@@ -718,9 +715,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 +743,9 @@ public class ServiceProviderMapper {
                        super(wrapped, null);
                }
 
-               public String getHSNameFormatId() {
+               public String[] getNameMapperIds() {
 
-                       return ((RelyingParty) wrapped).getHSNameFormatId();
+                       return ((RelyingParty) wrapped).getNameMapperIds();
                }
 
                public URL getAAUrl() {