From 3fc853cc1e861f455888a92ff0c9995270d2ba2c Mon Sep 17 00:00:00 2001 From: root Date: Mon, 25 Jul 2011 20:14:02 +0200 Subject: imported 1.1.2 --- .../shibboleth/wayf/DiscoveryServiceHandler.html | 2021 ++++++++++---------- 1 file changed, 1017 insertions(+), 1004 deletions(-) (limited to 'doc/src-xref/edu/internet2/middleware/shibboleth/wayf/DiscoveryServiceHandler.html') diff --git a/doc/src-xref/edu/internet2/middleware/shibboleth/wayf/DiscoveryServiceHandler.html b/doc/src-xref/edu/internet2/middleware/shibboleth/wayf/DiscoveryServiceHandler.html index a9e9e2f..cb90575 100644 --- a/doc/src-xref/edu/internet2/middleware/shibboleth/wayf/DiscoveryServiceHandler.html +++ b/doc/src-xref/edu/internet2/middleware/shibboleth/wayf/DiscoveryServiceHandler.html @@ -1,1016 +1,1029 @@ - + DiscoveryServiceHandler xref
View Javadoc
 
-1   /**
-2    * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
-3    *
-4    * Licensed under the Apache License, Version 2.0 (the "License");
-5    * you may not use this file except in compliance with the License.
-6    * You may obtain a copy of the License at
-7    *
-8    * http://www.apache.org/licenses/LICENSE-2.0
-9    *
-10   * Unless required by applicable law or agreed to in writing, software
-11   * distributed under the License is distributed on an "AS IS" BASIS,
-12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-13   * See the License for the specific language governing permissions and
-14   * limitations under the License.
-15   */
-16  package edu.internet2.middleware.shibboleth.wayf;
-17  
-18  import java.io.IOException;
-19  import java.io.UnsupportedEncodingException;
-20  import java.net.MalformedURLException;
-21  import java.net.URL;
-22  import java.net.URLDecoder;
-23  import java.net.URLEncoder;
-24  import java.util.ArrayList;
-25  import java.util.Collection;
-26  import java.util.Comparator;
-27  import java.util.Date;
-28  import java.util.HashSet;
-29  import java.util.Hashtable;
-30  import java.util.List;
-31  import java.util.Locale;
-32  import java.util.Map;
-33  import java.util.Set;
-34  import java.util.TreeSet;
-35  
-36  import javax.servlet.RequestDispatcher;
-37  import javax.servlet.ServletException;
-38  import javax.servlet.http.HttpServletRequest;
-39  import javax.servlet.http.HttpServletResponse;
-40  
-41  import org.opensaml.saml2.common.Extensions;
-42  import org.opensaml.saml2.metadata.EntityDescriptor;
-43  import org.opensaml.saml2.metadata.RoleDescriptor;
-44  import org.opensaml.saml2.metadata.SPSSODescriptor;
-45  import org.opensaml.samlext.idpdisco.DiscoveryResponse;
-46  import org.opensaml.xml.XMLObject;
-47  import org.slf4j.Logger;
-48  import org.slf4j.LoggerFactory;
-49  import org.w3c.dom.Element;
-50  import org.w3c.dom.NodeList;
-51  
-52  import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
-53  import edu.internet2.middleware.shibboleth.wayf.plugins.Plugin;
-54  import edu.internet2.middleware.shibboleth.wayf.plugins.PluginContext;
-55  import edu.internet2.middleware.shibboleth.wayf.plugins.PluginMetadataParameter;
-56  import edu.internet2.middleware.shibboleth.wayf.plugins.WayfRequestHandled;
-57  
-58  /**
-59    * Specific handler for each version of the Discovery Service. 
-60    */
-61  public class DiscoveryServiceHandler {
-62  
-63      /*
-64       * Protcol parameters - Old.
-65       */
-66      /**
-67       * Shire is the SP Assertion Consumer endpoint.
-68       */
-69      private static final String SHIRE_PARAM_NAME = "shire";
-70      /**
-71       * TargetName is where we are trying to get to.
-72       */
-73      private static final String TARGET_PARAM_NAME = "target";
-74      /**
-75       * time is to do with replay attack.
-76       */
-77      private static final String TIME_PARAM_NAME = "time";
-78      /**
-79       * This is the ID (in the metadata) of the SP.
-80       */
-81      private static final String PROVIDERID_PARAM_NAME = "providerId";
-82      
-83      /*
-84       * Protocol parameters - New
-85       */
-86      /**
-87       * The SP id.
-88       */
-89      private static final String ENTITYID_PARAM_NAME = "entityID";
-90      /**
-91       * Where to send the request back to.
-92       */
-93      private static final String RETURN_PARAM_NAME = "return";
-94      /**
-95       * "return" is an invalid attribute, so we use returnX.
-96       */
-97      private static final String RETURN_ATTRIBUTE_NAME = "returnX";
-98      /**
-99       * Alternatively the index of where to send the address back to.
-100      */
-101     private static final String RETURN_INDEX_NAME = "returnIndex";
-102     
-103     /**
-104      * What value to put the ID of the selected metadata into.
-105      */
-106     private static final String RETURNID_PARAM_NAME = "returnIDParam";
-107     
-108     /**
-109      * What returnIDParam defaults to.
-110      */
-111     private static final String RETURNID_DEFAULT_VALUE = "entityID";
-112     /**
-113      * Whether we are allowed to interact.
-114      */
-115     private static final String ISPASSIVE_PARAM_NAME = "isPassive";
-116     
-117     /**
-118      * Whether we understand this or not.
-119      */
-120     private static final String POLICY_PARAM_NAME = "policy";
-121 
-122     /**
-123      * The only policy we know about.
-124      */
-125     private static final String KNOWN_POLICY_NAME 
-126         = "urn:oasis:names:tc:SAML:profiles:SSO:idp-discoveryprotocol:single";
-127     
-128     /**
-129      * Mandatory Serialization constant.
-130      */
-131     private static final  Logger LOG = LoggerFactory.getLogger(DiscoveryServiceHandler.class.getName());
-132 
-133     /**
-134      * The location defines the last part of the URL which distinguished this handler. 
-135      */
-136     private final String location;
-137     
-138     /**
-139      * If isDefault is true then if there is a mismatch then this handler is used.  
-140      */
-141     private final boolean isDefault;
-142     
-143     /**
-144      * Config handles detailed behavior.
-145      */
-146     private final HandlerConfig config;
-147     
-148     /**
-149      * The list of all the metadata providers that this discovery handler believes in.
-150      */
-151     private final List <IdPSiteSet> siteSets;
-152     
-153     /**
-154      * The list of all the plugins that this hanlder has had configured.
-155      */
-156     private final List <Plugin> plugins;
-157     
-158     /**
-159      * Constructor to create and configure the handler.
-160      * @param config - DOM Element with configuration information.
-161      * @param federations - Supplies all known providers which will be included if so configured.
-162      * @param plugins - Supplies all known plugins which will be included if configured in. 
-163      * @param defaultConfig - The default configurations.
-164      * @throws ShibbolethConfigurationException - if we find something odd in the config file. 
-165      */
-166     protected DiscoveryServiceHandler(Element config, 
-167                                       Hashtable <String, IdPSiteSet> federations,
-168                                       Hashtable <String, Plugin> plugins, 
-169                                       HandlerConfig defaultConfig) throws ShibbolethConfigurationException
-170     {
-171         siteSets = new ArrayList <IdPSiteSet>(federations.size());
-172         this.plugins = new ArrayList <Plugin>(plugins.size());
-173 
-174         //
-175         // Collect the Configuration from the XML
-176         //
-177         
-178         this.config = new HandlerConfig(config, defaultConfig);
-179         
-180         location = config.getAttribute("location");
-181         
-182         if (location == null || location.equals("")) {
-183                 
-184                 LOG.error("DiscoveryService must have a location specified");
-185                 throw new ShibbolethConfigurationException("DiscoveryService must have a location specified");  
-186         }
-187         
-188         //
-189         // Is this the default WAYF?
-190         //
-191         
-192         String attribute = config.getAttribute("default");
-193         if (attribute != null && !attribute.equals("")) {
-194                 isDefault = Boolean.valueOf(attribute).booleanValue();
-195         } else {
-196                 isDefault = false;
-197         }
-198         
-199         //
-200         // Which federations (sitesets) do we care about?
-201         //
-202         
-203         NodeList list = config.getElementsByTagName("Federation");
-204                 
-205         for (int i = 0; i < list.getLength(); i++ ) {
-206                     
-207             attribute = ((Element) list.item(i)).getAttribute("identifier");
-208                     
-209                 IdPSiteSet siteset = federations.get(attribute);
-210                 
-211                 if (siteset == null) {
-212                     LOG.error("Handler " + location + ": could not find metadata for <Federation> with identifier " + attribute + ".");
-213                     throw new ShibbolethConfigurationException(
-214                            "Handler " + location + ": could not find metadata for  <Federation> identifier " + attribute + ".");
-215                 }
-216                 
-217                 siteSets.add(siteset);
-218         }
-219 
-220         if (siteSets.size() == 0) {
-221             //
-222             // No Federations explicitly named pick em all
-223             //
-224             siteSets.addAll(federations.values());
-225         }
-226         
-227         //
-228         // Now, which plugins?
-229         //
-230 
-231         list = config.getElementsByTagName("PluginInstance");
-232         
-233         for (int i = 0; i < list.getLength(); i++ ) {
-234                     
-235             attribute = ((Element) list.item(i)).getAttribute("identifier");
-236                     
-237                 Plugin plugin = plugins.get(attribute);
-238                 
-239                 if (plugin == null) {
-240                     LOG.error("Handler " + location + ": could not find plugin for identifier " + attribute);
-241                     throw new ShibbolethConfigurationException(
-242                               "Handler " + location + ": could not find plugin for identifier " + attribute);
-243                 }
-244                 
-245                 this.plugins.add(plugin);
-246         }
-247 
-248         //
-249         // So now tell every IdPSite about every plugin.
-250         //
-251         // Note that there is only one idpsite per metadatafile per WAYF and that the discovery 
-252         // services share them, so the data explosion is only number(IdpSites) * number(Plugins) not
-253         // number(IdpSites) * number(Plugins) * number(DiscoverHandlers)
-254         
-255         for (IdPSiteSet site: siteSets) {
-256             for (Plugin plugin: this.plugins) {
-257                 site.addPlugin(plugin);
-258             }
-259         }
-260     }
-261     
-262     
-263     //
-264     // Standard Beany Methods
-265     //
-266     /**
-267      * The 'Name' of the service. the path used to identify the ServiceHandler.
-268      * @return the path used to identify the ServiceHandler.
-269      */
-270     
-271     protected String getLocation() {
-272         return location;
-273     }
-274 
-275     /**
-276      * Whether this is the default service.
-277      * @return is it?
-278      */
-279     protected boolean isDefault() {
-280         return isDefault;
-281     }
-282     
-283     //
-284     // Now the bits that deal with the user request
-285     //
-286 
-287     public void doGet(HttpServletRequest req, HttpServletResponse res) {
-288         
-289         String policy = req.getParameter(POLICY_PARAM_NAME);
-290         
-291         if (null != policy && !KNOWN_POLICY_NAME.equals(policy)) {
-292             //
-293             // unknown policy
-294             //
-295             LOG.error("Unknown policy " + policy);
-296             handleError(req, res, "Unknown policy " + policy);
-297             return;
-298         }
-299         
-300         //
-301         // Decide how to route the request based on query string
-302         //
-303         String requestType = req.getParameter("action");
-304         
-305         if (requestType == null || requestType.equals("")) {
-306             requestType = "lookup";
-307         }
-308         
-309         try {
-310 
-311             if (requestType.equals("search")) {
-312                     
-313                 String parameter = req.getParameter("string"); 
-314                 if (parameter != null && parameter.equals("")) {
-315                         parameter = null;
-316                 }
-317                 handleLookup(req, res, parameter);
-318                     
-319             } else if (requestType.equals("selection")) {
-320                     
-321                 handleSelection(req, res);
-322             } else {
-323                 handleLookup(req, res, null);
-324             }
-325         } catch (WayfException we) {
-326             LOG.error("Error processing DS request:", we);
-327             handleError(req, res, we.getLocalizedMessage());
-328         } catch (WayfRequestHandled we) {
-329             //
-330             // Yuck - a sucess path involving an exception
-331             //
-332         }
-333 
-334     }
-335         
-336     /**
-337      * When the WAYF user has selected something we look it up, tell the plugins and then dispatch to the Idp.
-338      *  
-339      * @param req - standard J2EE stuff
-340      * @param res - standard J2EE stuff
-341      * @throws WayfRequestHandled - if one of the plugins has done the dispatch
-342      * @throws WayfException - if we had an errors
-343      */
-344     private void handleSelection(HttpServletRequest req, 
-345                                  HttpServletResponse res) throws WayfRequestHandled, WayfException 
-346      {
-347             
-348         String idpName = req.getParameter("origin");
-349         LOG.debug("Processing handle selection: " + idpName);
-350 
-351         String sPName = getSPId(req);
-352 
-353         if (idpName == null || idpName.equals("")) {
-354             handleLookup(req, res, null);
-355             return;
-356         }
-357 
-358         if (getValue(req, SHIRE_PARAM_NAME) == null) {
-359             //
-360             // 2.0 protocol
-361             //
-362             setupReturnAddress(sPName, req);
-363         }
-364         //
-365         // Notify plugins
-366         //
-367         IdPSite site = null; 
-368         
-369         for (Plugin plugin:plugins) {
-370             for (IdPSiteSet idPSiteSet: siteSets) {
-371                 PluginMetadataParameter param = idPSiteSet.paramFor(plugin);
-372                 plugin.selected(req, res, param, idpName);
-373                 if (site == null && idPSiteSet.containsIdP(idpName)) {
-374                     site = idPSiteSet.getSite(idpName);
-375                 }
-376             }
-377         }
-378         
-379         if (site == null) {
-380             handleLookup(req, res, null);
-381         } else {
-382             forwardRequest(req, res, site);
-383         }
-384     }
-385 
-386 
-387     /**
-388      * This sets up the parameter RETURN_ATTRIBUTE_NAME with the return address 
-389      * harvested from the reqest.
-390      * <ul><le>If a "return" parameter is present we check in the metadata for spoofing 
-391      *         and then set up from there </le>
-392      *     <le>If "returnID" is specified we get this from the metadata</le>
-393      *     <le>If nothing is provided we get the default from the metadata (if provided)</le>
-394      *     <le>Otherwise we whine</le>
-395      * </ul>     
-396      * @param spName - the name of the Service provider.
-397      * @param req - The request.
-398      * @throws WayfException - if we spot spoofing or there is no defaumlt 
-399      */
-400     private void setupReturnAddress(String spName, HttpServletRequest req) throws WayfException{
-401         
-402         DiscoveryResponse[] discoveryServices;
-403         Set<XMLObject> objects = new HashSet<XMLObject>();
-404         String defaultName = null;
-405         
-406         for (IdPSiteSet metadataProvider:siteSets) {
-407             
-408             //
-409             // Only do work if the SP makes sense
-410             //
-411 
-412             if (metadataProvider.containsSP(spName)) {
-413                
-414                 //
-415                 // The name makes sense so let's get the entity and from that
-416                 // all of its roles
-417                 //
-418                 
-419                 EntityDescriptor entity = metadataProvider.getEntity(spName);
-420                 List<RoleDescriptor> roles = entity.getRoleDescriptors();
-421                 
-422                 for (RoleDescriptor role:roles) {
-423                     
-424                     //
-425                     // Check every role
-426                     //
-427                     
-428                     if (role instanceof SPSSODescriptor) {
-429                         
-430                         //
-431                         // And grab hold of all the extensions for SPSSO descriptors
-432                         //
-433                         
-434                         Extensions exts = role.getExtensions();
-435                         if (exts != null) {
-436                             objects.addAll(exts.getOrderedChildren());
-437                         }
-438                     }
-439                 }
-440             }
-441         }
-442         
-443         //
-444         // Now, let's strip out everything which isn't a DiscoveryService
-445         //
-446         
-447         discoveryServices = new DiscoveryResponse[objects.size()];
-448         int dsCount = 0;
-449         
-450         for (XMLObject obj:objects) {
-451             if (obj instanceof DiscoveryResponse) {
-452                 DiscoveryResponse ds = (DiscoveryResponse) obj;
-453                 discoveryServices[dsCount++] = ds;
-454                 if (ds.isDefault() || null == defaultName) {
-455                     defaultName = ds.getLocation();
-456                 }
-457             }
-458         }
-459         
-460         //
-461         // Now process the return parameters.  The name is either a parameter
-462         // called RETURN_PARAM_NAME or an attributes called RETURN_ATTRIBUTE_NAME
-463         //
-464         String returnName = req.getParameter(RETURN_PARAM_NAME);
-465         
-466         if (returnName == null || returnName.length() == 0) {
-467             returnName = getValue(req, RETURN_ATTRIBUTE_NAME);
-468         }
-469         
-470         //
-471         // Return index is only ever a parameter
-472         //
-473         
-474         String returnIndex = req.getParameter(RETURN_INDEX_NAME);
-475         
-476         if (returnName != null && returnName.length() != 0) {
-477             //
-478             // Given something so we have to police it.
-479             //
-480             String nameNoParam = returnName;
-481             URL providedReturnURL;
-482             int index = nameNoParam.indexOf('?');
-483             boolean found = false;
-484             
-485             if (index >= 0) {
-486                 nameNoParam = nameNoParam.substring(0,index);
-487             }
-488             
-489             try {
-490                 providedReturnURL = new URL(nameNoParam);                
-491             } catch (MalformedURLException e) {
-492                 throw new WayfException("Couldn't parse provided return name " + nameNoParam, e);
-493             }
-494             
-495             
-496             for (DiscoveryResponse disc: discoveryServices) {
-497                 if (equalsURL(disc, providedReturnURL)) {
-498                     found = true;
-499                     break;
-500                 }
-501             }
-502             if (!found) {
-503                 throw new WayfException("Couldn't find endpoint " + nameNoParam + " in metadata");
-504             }
-505         } else if (returnIndex != null && returnIndex.length() != 0) {
-506             
-507             int index; 
-508             try {
-509                 index = Integer.parseInt(returnIndex);
-510             } catch (NumberFormatException e) {
-511                 throw new WayfException("Couldn't convert " + returnIndex + " into an index");
-512             }
-513             //
-514             // So look throught to find the endpoint with the correct index
-515             //
-516 
-517             boolean found = false;
-518             
-519             for (DiscoveryResponse disc: discoveryServices) {
-520                 if (index  == disc.getIndex()) {
-521                     found = true;
-522                     returnName = disc.getLocation();
-523                     break;
-524                 }
-525             }
-526             if (!found) {
-527                 throw new WayfException("Couldn't not find endpoint " + returnIndex + "in metadata");
-528             }
-529         } else {
-530             //
-531             // No name, not index, so we want the default
-532             //
-533             returnName = defaultName;
-534         }
-535         //
-536         // So by now returnName has the correct value, either harvested from or
-537         // policed against the metadata
-538         //
-539         req.setAttribute(RETURN_ATTRIBUTE_NAME, returnName);
-540     }
-541 
-542     /**
-543      * Helper function to see whather the provided endpoint in the metadata matches the 
-544      * provided return URL in the request.
-545      * 
-546      * @param discovery
-547      * @param providedName
-548      * @return
-549      */
-550     private static boolean equalsURL(DiscoveryResponse discovery, URL providedName) {
-551         
-552         //
-553         // Nothing provided - no match
-554         //
-555         if (null == discovery) {
-556             return false;
-557         }
-558         
-559         URL discoveryName;
-560         try {
-561             discoveryName = new URL(discovery.getLocation());
-562         } catch (MalformedURLException e) {
-563             //
-564             // Something bad happened.  Log it (it is only of interest to the sysadmin, not to the user)
-565             //
-566             LOG.warn("Found invalid discovery end point : " + discovery.getLocation(), e);
-567             return false;
-568         }
-569         
-570         return providedName.equals(discoveryName);
-571         
-572     }
-573 
-574     /**
-575      * Displays a Discovery Service selection page, having first consulted the plugins as needed.
-576      * @param req Describes the request
-577      * @param res Describes the response
-578      * @param searchName What are we looking for?
-579      * 
-580      * @throws WayfRequestHandled if a plugin has dealt with the request
-581      * @throws WayfException in case of an error.
-582      */
-583     private void handleLookup(HttpServletRequest req, 
-584                               HttpServletResponse res, 
-585                               String searchName) throws WayfException, WayfRequestHandled {
-586         
-587         String shire = getValue(req, SHIRE_PARAM_NAME);
-588         String providerId = getSPId(req);
-589         boolean twoZeroProtocol = (shire == null);
-590         boolean isPassive = (twoZeroProtocol && 
-591                              "true".equalsIgnoreCase(getValue(req, ISPASSIVE_PARAM_NAME)));
-592 
-593         Collection <IdPSiteSetEntry> siteLists = null;
-594         Collection<IdPSite> searchResults = null;
-595         
-596         if (config.getProvideListOfLists()) {
-597             siteLists = new ArrayList <IdPSiteSetEntry>(siteSets.size());
-598         }
-599 
-600         Collection <IdPSite> sites = null;
-601         Comparator<IdPSite> comparator = new IdPSite.Compare(req);
-602        
-603         if (config.getProvideList()) {
-604             sites = new TreeSet<IdPSite>(comparator);
-605         }
-606 
-607         if (searchName != null && !searchName.equals("")) {
-608             searchResults = new TreeSet<IdPSite>(comparator);
-609         }
-610 
-611         LOG.debug("Processing Idp Lookup for : " + providerId);
-612 
-613         //
-614         // Iterate over all the sitesets and if they know about the SP pass them to the plugins
-615         // and then add them too the list
-616         //
-617 
-618         PluginContext[] ctx = new PluginContext[plugins.size()];
-619         List<IdPSite> hintList = new ArrayList<IdPSite>();
-620         
-621         if (twoZeroProtocol) {
-622             setupReturnAddress(providerId, req);
-623         }
-624         //
-625         // By having siteLists and sites as parameters we only iterate over 
-626         // the metadata arrays once.
-627         //
-628         try {   
-629             for (IdPSiteSet metadataProvider:siteSets) {
-630                 
-631                 //
-632                 // Only do work if the SP makes sense
-633                 //
-634 
-635                 if (metadataProvider.containsSP(providerId) || !config.getLookupSp()) {
-636 
-637                     Collection <IdPSite> search = null;
-638                     
-639                     if (searchResults != null) {
-640                         search = new TreeSet<IdPSite>(comparator);
-641                     }
-642 
-643                     Map <String, IdPSite> theseSites = metadataProvider.getIdPSites(searchName, config, search);
-644                     
-645                     //
-646                     // Consult the plugins
-647                     //
-648                     for (int i = 0; i < plugins.size(); i++) {
-649                             
-650                         Plugin plugin = plugins.get(i);
-651                         
-652                         if (searchResults == null) {
-653                             //
-654                             // This was a search
-655                             //
-656                             ctx[i] = plugin.lookup(req, 
-657                                                    res, 
-658                                                    metadataProvider.paramFor(plugin), 
-659                                                    theseSites, 
-660                                                    ctx[i], 
-661                                                    hintList);
-662                         } else {
-663                             ctx[i] = plugin.search(req, 
-664                                                    res, 
-665                                                    metadataProvider.paramFor(plugin), 
-666                                                    searchName, 
-667                                                    theseSites, 
-668                                                    ctx[i], 
-669                                                    searchResults, 
-670                                                    hintList);
-671                         }
-672                     }
-673                     
-674                     if (null == theseSites || theseSites.isEmpty()) {
-675                         continue;
-676                     }
-677                     
-678                     //
-679                         
-680                     // Accumulate any per-metadata provider information
-681                     // 
-682             
-683                     Collection<IdPSite> values = new TreeSet<IdPSite>(comparator);
-684                     if (null != theseSites) {
-685                         values.addAll(theseSites.values());
-686                     }
-687                     
-688                     if (siteLists != null) {
-689                         siteLists.add(new IdPSiteSetEntry(metadataProvider,values));
-690                     }
-691                             
-692                     if (sites != null) {
-693                         sites.addAll(values);
-694                     }
-695                     
-696                     if (searchResults != null) {
-697                         searchResults.addAll(search);
-698                     }
-699                 }
-700             }
-701             
-702             if (isPassive) {
-703                 //
-704                 // No GUI intervention.
-705                 //
-706                 if (0 != hintList.size()) {
-707                     //
-708                     // We found a candidate, hand it back
-709                     //
-710                     forwardRequest(req, res, hintList.get(0));
-711                 } else {
-712                     forwardRequest(req, res, null);
-713                 }   
-714                 return;
-715             }
-716             
-717             //
-718             // Now set up all the funky stuff that the JSP needs.  Firstly the protocol
-719             // specific parameters which will come back to us
-720             //
-721             
-722             if (twoZeroProtocol) {
-723                 //
-724                 // The return address was set up in setupReturnAddress
-725                 //
-726                 String returnString = (String) req.getAttribute(RETURN_ATTRIBUTE_NAME);
-727                 if (null == returnString || 0 == returnString.length()) {
-728                     throw new WayfException("Parameter " + RETURN_PARAM_NAME + " not supplied");
-729                 }
-730 
-731                 String returnId = getValue(req, RETURNID_PARAM_NAME);
-732                 if (null == returnId || 0 == returnId.length()) {
-733                     returnId = RETURNID_DEFAULT_VALUE;
-734                 }
-735                 //
-736                 // Return *means* something so we cannot use it as an attribute
-737                 //
-738                 req.setAttribute(RETURN_ATTRIBUTE_NAME, returnString);
-739                 req.setAttribute(RETURNID_PARAM_NAME, returnId);
-740                 req.setAttribute(ENTITYID_PARAM_NAME, providerId);
-741                 
-742             } else {
-743                 String target = getValue(req, TARGET_PARAM_NAME);
-744                 if (null == target || 0 == target.length()) {
-745                     throw new WayfException("Could not extract target from provided parameters");
-746                 }
-747                 req.setAttribute(SHIRE_PARAM_NAME, shire);
-748                 req.setAttribute(TARGET_PARAM_NAME, target);
-749                 req.setAttribute(PROVIDERID_PARAM_NAME, providerId);
-750                 //
-751                 // Time is in unix format
-752                 //
-753                 req.setAttribute("time", new Long(new Date().getTime() / 1000).toString()); 
-754                 
-755             }
-756             
-757             //
-758             // Setup the stuff that the GUI wants.  
-759             //
-760                 
-761             setDisplayLanguage(sites, req);
-762             req.setAttribute("sites", sites);
-763             if (null != siteLists) {
-764                 for (IdPSiteSetEntry siteSetEntry:siteLists) {
-765                     setDisplayLanguage(siteSetEntry.getSites(), req);
-766                 }
-767             }
-768                 
-769             req.setAttribute("siteLists", siteLists);
-770             req.setAttribute("requestURL", req.getRequestURI().toString());
-771 
-772             if (searchResults != null) {
-773                 if (searchResults.size() != 0) {
-774                     setDisplayLanguage(searchResults, req);
-775                     req.setAttribute("searchresults", searchResults);
-776                 } else {
-777                     req.setAttribute("searchResultsEmpty", "true");
-778                 }
-779             }
-780 
-781             if (hintList.size() > 0) {
-782                 setDisplayLanguage(hintList, req);
-783                 req.setAttribute("cookieList", hintList);
-784             }
-785 
-786             LOG.debug("Displaying WAYF selection page.");
-787             RequestDispatcher rd = req.getRequestDispatcher(config.getJspFile());
-788 
-789             //
-790             // And off to the jsp
-791             //
-792             rd.forward(req, res);
-793         } catch (IOException ioe) {
-794             LOG.error("Problem displaying WAYF UI.\n" +  ioe.getMessage());
-795             throw new WayfException("Problem displaying WAYF UI", ioe);
-796         } catch (ServletException se) {
-797             LOG.error("Problem displaying WAYF UI.\n" +  se.getMessage());
-798             throw new WayfException("Problem displaying WAYF UI", se);
-799         }
-800     }
-801 
-802     /**
-803      * Prior to display we set the display language from the
-804      * browser. There is probably a proper way to do this using
-805      * jsp, but I want to keep the API between JSP and java the same 1.3->2.0
-806      * @param sites - the sites we need to impact
-807      * @param req - from whiuch we get the locale
-808      */
-809     private void setDisplayLanguage(Collection<IdPSite> sites, HttpServletRequest req) {
-810         
-811         if (null == sites) {
-812             return;
-813         }
-814         Locale locale = req.getLocale();
-815         if (null == locale) {
-816             Locale.getDefault();
-817         }
-818         String lang = locale.getLanguage();
-819 
-820         for (IdPSite site : sites) {
-821             site.setDisplayLanguage(lang);
-822         }
-823     }
-824 
-825 
-826     /**
-827      * Uses an HTTP Status 307 redirect to forward the user to the IdP or the SP.
-828      * 
-829      * @param req request under consideration
-830      * @param res response under construction
-831      * @param site The Idp
-832      * @throws WayfException if bad things happen.
-833      */
-834     public static void forwardRequest(HttpServletRequest req, HttpServletResponse res, IdPSite site)
-835                     throws WayfException {
-836 
-837         String shire = getValue(req, SHIRE_PARAM_NAME);
-838         String providerId = getSPId(req);
-839         boolean twoZeroProtocol = (shire == null);
-840 
-841         if (!twoZeroProtocol) {
-842             String handleService = site.getAddressForWAYF(); 
-843             if (handleService != null ) {
-844 
-845                 String target = getValue(req, TARGET_PARAM_NAME);
-846                 if (null == target || 0 == target.length()) {
-847                     throw new WayfException("Could not extract target from provided parameters");
-848                 }
-849 
-850                 LOG.info("Redirecting to selected Handle Service: " + handleService);
-851                 try {
-852                     StringBuffer buffer = new StringBuffer(handleService +  
-853                        "?" + TARGET_PARAM_NAME + "=");
-854                     buffer.append(URLEncoder.encode(target, "UTF-8"));
-855                     buffer.append("&" + SHIRE_PARAM_NAME + "=");
-856                     buffer.append(URLEncoder.encode(shire, "UTF-8"));
-857                     buffer.append("&" + PROVIDERID_PARAM_NAME + "=");
-858                     buffer.append(URLEncoder.encode(providerId, "UTF-8"));
-859                          
-860                     //
-861                     // Time is as per U**X
-862                     //
-863                     buffer.append("&" +  TIME_PARAM_NAME + "=");
-864                     buffer.append(new Long(new Date().getTime() / 1000).toString());
-865                     res.sendRedirect(buffer.toString());
-866                 } catch (IOException ioe) {
-867                     //
-868                     // That failed.  
-869                     //
-870                     throw new WayfException("Error forwarding to IdP: \n" + ioe.getMessage());
-871                 }
-872             } else {
-873                 String s = "Error finding to IdP: " + site.getDisplayName(req); 
-874                 LOG.error(s);
-875                 throw new WayfException(s);
-876             }
-877         } else {
-878             String returnUrl = (String) req.getAttribute(RETURN_ATTRIBUTE_NAME);
-879             
-880             if (null == returnUrl || 0 == returnUrl.length()) {
-881                 throw new WayfException("Could not find return parameter");
-882             }
-883             try {
-884                 returnUrl = URLDecoder.decode(returnUrl, "UTF-8");
-885             } catch (UnsupportedEncodingException e) {
-886                 throw new WayfException("Did not understand parameter ", e);
-887             }
-888             String redirect;
-889             if (site != null) {
-890                 StringBuffer buffer = new StringBuffer(returnUrl);
-891                 //
-892                 // If we were given anybody to lookup, construct the URL
-893                 //
-894                 String returnParam = getValue(req, RETURNID_PARAM_NAME);
-895                 if (null == returnParam || 0 == returnParam.length()) {
-896                     returnParam = RETURNID_DEFAULT_VALUE;
-897                 }              
-898                 //
-899                 // Do we add a '?' or a '&' for the parameters
-900                 //
-901 
-902                 if (returnUrl.indexOf('?') >= 0) {
-903                     //
-904                     // there is a parameter already.  Add a '&'
-905                     //
-906                     buffer.append("&" + returnParam + "=");
-907                 } else {
-908                     //
-909                     // No parameter.  Use ?
-910                     //
-911                     buffer.append("?" + returnParam + "=");
-912                 }
-913                 buffer.append(site.getName());
-914                 redirect =  buffer.toString();
-915             } else {
-916                 //
-917                 // Just send it back
-918                 //
-919                 redirect = returnUrl;
-920             }
-921             
-922             LOG.debug("Dispatching to " + redirect);
-923             
-924             try {
-925                 res.sendRedirect(redirect);
-926             } catch (IOException ioe) {
-927                 //
-928                 // That failed.  
-929                 //
-930                 throw new WayfException("Error forwarding back to Sp: \n" + ioe.getMessage());
-931             }         
-932         }
-933     }
-934 
-935     /**
-936      * Handles all "recoverable" errors in WAYF processing by logging the error and forwarding the user to an
-937      * appropriate error page.
-938      * 
-939      * @param req request under consideration
-940      * @param res response under construction
-941      * @param message - what so say
-942      */
-943     private void handleError(HttpServletRequest req, HttpServletResponse res, String message) {
-944 
-945         LOG.debug("Displaying WAYF error page.");
-946         req.setAttribute("errorText", message);
-947         req.setAttribute("requestURL", req.getRequestURI().toString());
-948         RequestDispatcher rd = req.getRequestDispatcher(config.getErrorJspFile());
-949 
-950         try {
-951             rd.forward(req, res);
-952         } catch (IOException ioe) {
-953             LOG.error("Problem trying to display WAYF error page: " + ioe.toString());
-954         } catch (ServletException se) {
-955             LOG.error("Problem trying to display WAYF error page: " + se.toString());
-956         }
-957     }
-958 
-959     /**
-960      * Gets the value for the parameter either from the parameter or from jsp.
-961      * @param req - the request.
-962      * @param name - the name of the parameter.
-963      * @return - result
-964      */
-965     private static String getValue(HttpServletRequest req, String name) {
-966 
-967         
-968         String value = req.getParameter(name); 
-969         if (value != null) {
-970             return value;
-971         }
-972         return (String) req.getAttribute(name);
-973     }
-974 
-975     private static String getSPId(HttpServletRequest req) throws WayfException {
-976 
-977         //
-978         // Try first with 2.0 version
-979         //
-980         String param = req.getParameter(ENTITYID_PARAM_NAME);
-981         if (param != null && !(param.length() == 0)) {
-982             return param;
-983         } 
-984         
-985         param = (String) req.getAttribute(ENTITYID_PARAM_NAME);
-986         if (param != null && !(param.length() == 0)) {
-987             return param;
-988         }       
-989         //
-990         // So Try with 1.3 version
-991         //
-992         param = req.getParameter(PROVIDERID_PARAM_NAME);
-993         if (param != null && !(param.length() == 0)) {
-994             return param;
-995         } 
-996         
-997         param = (String) req.getAttribute(PROVIDERID_PARAM_NAME);
-998         if (param != null && !(param.length() == 0)) {
-999             return param;
-1000         } 
-1001         throw new WayfException("Could not locate SP identifier in parameters");
-1002     }   
-1003 }
+1   /**
+2    * Copyright [2006] [University Corporation for Advanced Internet Development, Inc.]
+3    *
+4    * Licensed under the Apache License, Version 2.0 (the "License");
+5    * you may not use this file except in compliance with the License.
+6    * You may obtain a copy of the License at
+7    *
+8    * http://www.apache.org/licenses/LICENSE-2.0
+9    *
+10   * Unless required by applicable law or agreed to in writing, software
+11   * distributed under the License is distributed on an "AS IS" BASIS,
+12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+13   * See the License for the specific language governing permissions and
+14   * limitations under the License.
+15   */
+16  package edu.internet2.middleware.shibboleth.wayf;
+17  
+18  import java.io.IOException;
+19  import java.io.UnsupportedEncodingException;
+20  import java.net.MalformedURLException;
+21  import java.net.URL;
+22  import java.net.URLDecoder;
+23  import java.net.URLEncoder;
+24  import java.util.ArrayList;
+25  import java.util.Collection;
+26  import java.util.Comparator;
+27  import java.util.Date;
+28  import java.util.HashSet;
+29  import java.util.Hashtable;
+30  import java.util.List;
+31  import java.util.Locale;
+32  import java.util.Map;
+33  import java.util.Set;
+34  import java.util.TreeSet;
+35  
+36  import javax.servlet.RequestDispatcher;
+37  import javax.servlet.ServletException;
+38  import javax.servlet.http.HttpServletRequest;
+39  import javax.servlet.http.HttpServletResponse;
+40  
+41  import org.opensaml.saml2.common.Extensions;
+42  import org.opensaml.saml2.metadata.EntityDescriptor;
+43  import org.opensaml.saml2.metadata.RoleDescriptor;
+44  import org.opensaml.saml2.metadata.SPSSODescriptor;
+45  import org.opensaml.samlext.idpdisco.DiscoveryResponse;
+46  import org.opensaml.xml.XMLObject;
+47  import org.slf4j.Logger;
+48  import org.slf4j.LoggerFactory;
+49  import org.w3c.dom.Element;
+50  import org.w3c.dom.NodeList;
+51  
+52  import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
+53  import edu.internet2.middleware.shibboleth.wayf.plugins.Plugin;
+54  import edu.internet2.middleware.shibboleth.wayf.plugins.PluginContext;
+55  import edu.internet2.middleware.shibboleth.wayf.plugins.PluginMetadataParameter;
+56  import edu.internet2.middleware.shibboleth.wayf.plugins.WayfRequestHandled;
+57  
+58  /**
+59    * Specific handler for each version of the Discovery Service. 
+60    */
+61  public class DiscoveryServiceHandler {
+62  
+63      /*
+64       * Protcol parameters - Old.
+65       */
+66      /**
+67       * Shire is the SP Assertion Consumer endpoint.
+68       */
+69      private static final String SHIRE_PARAM_NAME = "shire";
+70      /**
+71       * TargetName is where we are trying to get to.
+72       */
+73      private static final String TARGET_PARAM_NAME = "target";
+74      /**
+75       * time is to do with replay attack.
+76       */
+77      private static final String TIME_PARAM_NAME = "time";
+78      /**
+79       * This is the ID (in the metadata) of the SP.
+80       */
+81      private static final String PROVIDERID_PARAM_NAME = "providerId";
+82      
+83      /**
+84       * The entityDescriptor for the SP (if present).
+85       */
+86      private static final String PROVIDERID_OBJECT_PARAM_NAME = "providerObject"; 
+87      /*
+88       * Protocol parameters - New
+89       */
+90      /**
+91       * The SP id.
+92       */
+93      private static final String ENTITYID_PARAM_NAME = "entityID";
+94      /**
+95       * Where to send the request back to.
+96       */
+97      private static final String RETURN_PARAM_NAME = "return";
+98      /**
+99       * "return" is an invalid attribute, so we use returnX.
+100      */
+101     private static final String RETURN_ATTRIBUTE_NAME = "returnX";
+102     /**
+103      * Alternatively the index of where to send the address back to.
+104      */
+105     private static final String RETURN_INDEX_NAME = "returnIndex";
+106     
+107     /**
+108      * What value to put the ID of the selected metadata into.
+109      */
+110     private static final String RETURNID_PARAM_NAME = "returnIDParam";
+111     
+112     /**
+113      * What returnIDParam defaults to.
+114      */
+115     private static final String RETURNID_DEFAULT_VALUE = "entityID";
+116     /**
+117      * Whether we are allowed to interact.
+118      */
+119     private static final String ISPASSIVE_PARAM_NAME = "isPassive";
+120     
+121     /**
+122      * Whether we understand this or not.
+123      */
+124     private static final String POLICY_PARAM_NAME = "policy";
+125 
+126     /**
+127      * The only policy we know about.
+128      */
+129     private static final String KNOWN_POLICY_NAME 
+130         = "urn:oasis:names:tc:SAML:profiles:SSO:idp-discoveryprotocol:single";
+131     
+132     /**
+133      * Mandatory Serialization constant.
+134      */
+135     private static final  Logger LOG = LoggerFactory.getLogger(DiscoveryServiceHandler.class.getName());
+136 
+137     /**
+138      * The location defines the last part of the URL which distinguished this handler. 
+139      */
+140     private final String location;
+141     
+142     /**
+143      * If isDefault is true then if there is a mismatch then this handler is used.  
+144      */
+145     private final boolean isDefault;
+146     
+147     /**
+148      * Config handles detailed behavior.
+149      */
+150     private final HandlerConfig config;
+151     
+152     /**
+153      * The list of all the metadata providers that this discovery handler believes in.
+154      */
+155     private final List <IdPSiteSet> siteSets;
+156     
+157     /**
+158      * The list of all the plugins that this hanlder has had configured.
+159      */
+160     private final List <Plugin> plugins;
+161     
+162     /**
+163      * Constructor to create and configure the handler.
+164      * @param config - DOM Element with configuration information.
+165      * @param federations - Supplies all known providers which will be included if so configured.
+166      * @param plugins - Supplies all known plugins which will be included if configured in. 
+167      * @param defaultConfig - The default configurations.
+168      * @throws ShibbolethConfigurationException - if we find something odd in the config file. 
+169      */
+170     protected DiscoveryServiceHandler(Element config, 
+171                                       Hashtable <String, IdPSiteSet> federations,
+172                                       Hashtable <String, Plugin> plugins, 
+173                                       HandlerConfig defaultConfig) throws ShibbolethConfigurationException
+174     {
+175         siteSets = new ArrayList <IdPSiteSet>(federations.size());
+176         this.plugins = new ArrayList <Plugin>(plugins.size());
+177 
+178         //
+179         // Collect the Configuration from the XML
+180         //
+181         
+182         this.config = new HandlerConfig(config, defaultConfig);
+183         
+184         location = config.getAttribute("location");
+185         
+186         if (location == null || location.equals("")) {
+187                 
+188                 LOG.error("DiscoveryService must have a location specified");
+189                 throw new ShibbolethConfigurationException("DiscoveryService must have a location specified");  
+190         }
+191         
+192         //
+193         // Is this the default WAYF?
+194         //
+195         
+196         String attribute = config.getAttribute("default");
+197         if (attribute != null && !attribute.equals("")) {
+198                 isDefault = Boolean.valueOf(attribute).booleanValue();
+199         } else {
+200                 isDefault = false;
+201         }
+202         
+203         //
+204         // Which federations (sitesets) do we care about?
+205         //
+206         
+207         NodeList list = config.getElementsByTagName("Federation");
+208                 
+209         for (int i = 0; i < list.getLength(); i++ ) {
+210                     
+211             attribute = ((Element) list.item(i)).getAttribute("identifier");
+212                     
+213                 IdPSiteSet siteset = federations.get(attribute);
+214                 
+215                 if (siteset == null) {
+216                     LOG.error("Handler " + location + ": could not find metadata for <Federation> with identifier " + attribute + ".");
+217                     throw new ShibbolethConfigurationException(
+218                            "Handler " + location + ": could not find metadata for  <Federation> identifier " + attribute + ".");
+219                 }
+220                 
+221                 siteSets.add(siteset);
+222         }
+223 
+224         if (siteSets.size() == 0) {
+225             //
+226             // No Federations explicitly named pick em all
+227             //
+228             siteSets.addAll(federations.values());
+229         }
+230         
+231         //
+232         // Now, which plugins?
+233         //
+234 
+235         list = config.getElementsByTagName("PluginInstance");
+236         
+237         for (int i = 0; i < list.getLength(); i++ ) {
+238                     
+239             attribute = ((Element) list.item(i)).getAttribute("identifier");
+240                     
+241                 Plugin plugin = plugins.get(attribute);
+242                 
+243                 if (plugin == null) {
+244                     LOG.error("Handler " + location + ": could not find plugin for identifier " + attribute);
+245                     throw new ShibbolethConfigurationException(
+246                               "Handler " + location + ": could not find plugin for identifier " + attribute);
+247                 }
+248                 
+249                 this.plugins.add(plugin);
+250         }
+251 
+252         //
+253         // So now tell every IdPSite about every plugin.
+254         //
+255         // Note that there is only one idpsite per metadatafile per WAYF and that the discovery 
+256         // services share them, so the data explosion is only number(IdpSites) * number(Plugins) not
+257         // number(IdpSites) * number(Plugins) * number(DiscoverHandlers)
+258         
+259         for (IdPSiteSet site: siteSets) {
+260             for (Plugin plugin: this.plugins) {
+261                 site.addPlugin(plugin);
+262             }
+263         }
+264     }
+265     
+266     
+267     //
+268     // Standard Beany Methods
+269     //
+270     /**
+271      * The 'Name' of the service. the path used to identify the ServiceHandler.
+272      * @return the path used to identify the ServiceHandler.
+273      */
+274     
+275     protected String getLocation() {
+276         return location;
+277     }
+278 
+279     /**
+280      * Whether this is the default service.
+281      * @return is it?
+282      */
+283     protected boolean isDefault() {
+284         return isDefault;
+285     }
+286     
+287     //
+288     // Now the bits that deal with the user request
+289     //
+290 
+291     public void doGet(HttpServletRequest req, HttpServletResponse res) {
+292         
+293         String policy = req.getParameter(POLICY_PARAM_NAME);
+294         
+295         if (null != policy && !KNOWN_POLICY_NAME.equals(policy)) {
+296             //
+297             // unknown policy
+298             //
+299             LOG.error("Unknown policy " + policy);
+300             handleError(req, res, "Unknown policy " + policy);
+301             return;
+302         }
+303         
+304         //
+305         // Decide how to route the request based on query string
+306         //
+307         String requestType = req.getParameter("action");
+308         
+309         if (requestType == null || requestType.equals("")) {
+310             requestType = "lookup";
+311         }
+312         
+313         try {
+314 
+315             if (requestType.equals("search")) {
+316                     
+317                 String parameter = req.getParameter("string"); 
+318                 if (parameter != null && parameter.equals("")) {
+319                         parameter = null;
+320                 }
+321                 handleLookup(req, res, parameter);
+322                     
+323             } else if (requestType.equals("selection")) {
+324                     
+325                 handleSelection(req, res);
+326             } else {
+327                 handleLookup(req, res, null);
+328             }
+329         } catch (WayfException we) {
+330             LOG.error("Error processing DS request:", we);
+331             handleError(req, res, we.getLocalizedMessage());
+332         } catch (WayfRequestHandled we) {
+333             //
+334             // Yuck - a sucess path involving an exception
+335             //
+336         }
+337 
+338     }
+339         
+340     /**
+341      * When the WAYF user has selected something we look it up, tell the plugins and then dispatch to the Idp.
+342      *  
+343      * @param req - standard J2EE stuff
+344      * @param res - standard J2EE stuff
+345      * @throws WayfRequestHandled - if one of the plugins has done the dispatch
+346      * @throws WayfException - if we had an errors
+347      */
+348     private void handleSelection(HttpServletRequest req, 
+349                                  HttpServletResponse res) throws WayfRequestHandled, WayfException 
+350      {
+351             
+352         String idpName = req.getParameter("origin");
+353         LOG.debug("Processing handle selection: " + idpName);
+354 
+355         String sPName = getSPId(req);
+356 
+357         if (idpName == null || idpName.equals("")) {
+358             handleLookup(req, res, null);
+359             return;
+360         }
+361 
+362         if (getValue(req, SHIRE_PARAM_NAME) == null) {
+363             //
+364             // 2.0 protocol
+365             //
+366             setupReturnAddress(sPName, req);
+367         }
+368         //
+369         // Notify plugins
+370         //
+371         IdPSite site = null; 
+372         
+373         for (Plugin plugin:plugins) {
+374             for (IdPSiteSet idPSiteSet: siteSets) {
+375                 PluginMetadataParameter param = idPSiteSet.paramFor(plugin);
+376                 plugin.selected(req, res, param, idpName);
+377                 if (site == null && idPSiteSet.containsIdP(idpName)) {
+378                     site = idPSiteSet.getSite(idpName);
+379                 }
+380             }
+381         }
+382         
+383         if (site == null) {
+384             handleLookup(req, res, null);
+385         } else {
+386             forwardRequest(req, res, site);
+387         }
+388     }
+389 
+390 
+391     /**
+392      * This sets up the parameter RETURN_ATTRIBUTE_NAME with the return address 
+393      * harvested from the reqest.
+394      * <ul><le>If a "return" parameter is present we check in the metadata for spoofing 
+395      *         and then set up from there </le>
+396      *     <le>If "returnID" is specified we get this from the metadata</le>
+397      *     <le>If nothing is provided we get the default from the metadata (if provided)</le>
+398      *     <le>Otherwise we whine</le>
+399      * </ul>     
+400      * @param spName - the name of the Service provider.
+401      * @param req - The request.
+402      * @throws WayfException - if we spot spoofing or there is no defaumlt 
+403      */
+404     private void setupReturnAddress(String spName, HttpServletRequest req) throws WayfException{
+405         
+406         DiscoveryResponse[] discoveryServices;
+407         Set<XMLObject> objects = new HashSet<XMLObject>();
+408         String defaultName = null;
+409         boolean foundSPName = false;
+410         
+411         for (IdPSiteSet metadataProvider:siteSets) {
+412             
+413             //
+414             // Only do work if the SP makes sense
+415             //
+416 
+417             if (metadataProvider.containsSP(spName)) {
+418                
+419                 //
+420                 // The name makes sense so let's get the entity and from that
+421                 // all of its roles
+422                 //
+423                 foundSPName = true;
+424                 EntityDescriptor entity = metadataProvider.getEntity(spName);
+425                 List<RoleDescriptor> roles = entity.getRoleDescriptors();
+426                 
+427                 for (RoleDescriptor role:roles) {
+428                     
+429                     //
+430                     // Check every role
+431                     //
+432                     
+433                     if (role instanceof SPSSODescriptor) {
+434                         
+435                         //
+436                         // And grab hold of all the extensions for SPSSO descriptors
+437                         //
+438                         
+439                         Extensions exts = role.getExtensions();
+440                         if (exts != null) {
+441                             objects.addAll(exts.getOrderedChildren());
+442                         }
+443                     }
+444                 }
+445             }
+446         }
+447         if (!foundSPName) {
+448             LOG.error("Could not locate SP " + spName + " in metadata");
+449         }
+450         
+451         //
+452         // Now, let's strip out everything which isn't a DiscoveryService
+453         //
+454         
+455         discoveryServices = new DiscoveryResponse[objects.size()];
+456         int dsCount = 0;
+457         
+458         for (XMLObject obj:objects) {
+459             if (obj instanceof DiscoveryResponse) {
+460                 DiscoveryResponse ds = (DiscoveryResponse) obj;
+461                 discoveryServices[dsCount++] = ds;
+462                 if (ds.isDefault() || null == defaultName) {
+463                     defaultName = ds.getLocation();
+464                 }
+465             }
+466         }
+467         
+468         //
+469         // Now process the return parameters.  The name is either a parameter
+470         // called RETURN_PARAM_NAME or an attributes called RETURN_ATTRIBUTE_NAME
+471         //
+472         String returnName = req.getParameter(RETURN_PARAM_NAME);
+473         
+474         if (returnName == null || returnName.length() == 0) {
+475             returnName = getValue(req, RETURN_ATTRIBUTE_NAME);
+476         }
+477         
+478         //
+479         // Return index is only ever a parameter
+480         //
+481         
+482         String returnIndex = req.getParameter(RETURN_INDEX_NAME);
+483         
+484         if (returnName != null && returnName.length() != 0) {
+485             //
+486             // Given something so we have to police it.
+487             //
+488             String nameNoParam = returnName;
+489             URL providedReturnURL;
+490             int index = nameNoParam.indexOf('?');
+491             boolean found = false;
+492             
+493             if (index >= 0) {
+494                 nameNoParam = nameNoParam.substring(0,index);
+495             }
+496             
+497             try {
+498                 providedReturnURL = new URL(nameNoParam);                
+499             } catch (MalformedURLException e) {
+500                 throw new WayfException("Couldn't parse provided return name " + nameNoParam, e);
+501             }
+502             
+503             
+504             for (DiscoveryResponse disc: discoveryServices) {
+505                 if (equalsURL(disc, providedReturnURL)) {
+506                     found = true;
+507                     break;
+508                 }
+509             }
+510             if (!found) {
+511                 throw new WayfException("Couldn't find endpoint " + nameNoParam + " in metadata");
+512             }
+513         } else if (returnIndex != null && returnIndex.length() != 0) {
+514             
+515             int index; 
+516             try {
+517                 index = Integer.parseInt(returnIndex);
+518             } catch (NumberFormatException e) {
+519                 throw new WayfException("Couldn't convert " + returnIndex + " into an index");
+520             }
+521             //
+522             // So look through to find the endpoint with the correct index
+523             //
+524             boolean found = false;
+525             
+526             for (DiscoveryResponse disc: discoveryServices) {
+527                 if (index  == disc.getIndex()) {
+528                     found = true;
+529                     returnName = disc.getLocation();
+530                     break;
+531                 }
+532             }
+533             if (!found) {
+534                 throw new WayfException("Couldn't not find endpoint " + returnIndex + "in metadata");
+535             }
+536         } else {
+537             //
+538             // No name, not index, so we want the default
+539             //
+540             returnName = defaultName;
+541         }
+542         //
+543         // So by now returnName has the correct value, either harvested from or
+544         // policed against the metadata
+545         //
+546         req.setAttribute(RETURN_ATTRIBUTE_NAME, returnName);
+547     }
+548 
+549     /**
+550      * Helper function to see whather the provided endpoint in the metadata matches the 
+551      * provided return URL in the request.
+552      * 
+553      * @param discovery
+554      * @param providedName
+555      * @return
+556      */
+557     private static boolean equalsURL(DiscoveryResponse discovery, URL providedName) {
+558         
+559         //
+560         // Nothing provided - no match
+561         //
+562         if (null == discovery) {
+563             return false;
+564         }
+565         
+566         URL discoveryName;
+567         try {
+568             discoveryName = new URL(discovery.getLocation());
+569         } catch (MalformedURLException e) {
+570             //
+571             // Something bad happened.  Log it (it is only of interest to the sysadmin, not to the user)
+572             //
+573             LOG.warn("Found invalid discovery end point : " + discovery.getLocation(), e);
+574             return false;
+575         }
+576         
+577         return providedName.equals(discoveryName);
+578         
+579     }
+580 
+581     /**
+582      * Displays a Discovery Service selection page, having first consulted the plugins as needed.
+583      * @param req Describes the request
+584      * @param res Describes the response
+585      * @param searchName What are we looking for?
+586      * 
+587      * @throws WayfRequestHandled if a plugin has dealt with the request
+588      * @throws WayfException in case of an error.
+589      */
+590     private void handleLookup(HttpServletRequest req, 
+591                               HttpServletResponse res, 
+592                               String searchName) throws WayfException, WayfRequestHandled {
+593         
+594         String shire = getValue(req, SHIRE_PARAM_NAME);
+595         String providerId = getSPId(req);
+596         EntityDescriptor sp = null;
+597         boolean twoZeroProtocol = (shire == null);
+598         boolean isPassive = (twoZeroProtocol && 
+599                              "true".equalsIgnoreCase(getValue(req, ISPASSIVE_PARAM_NAME)));
+600 
+601         Collection <IdPSiteSetEntry> siteLists = null;
+602         Collection<IdPSite> searchResults = null;
+603         
+604         if (config.getProvideListOfLists()) {
+605             siteLists = new ArrayList <IdPSiteSetEntry>(siteSets.size());
+606         }
+607 
+608         Collection <IdPSite> sites = null;
+609         Comparator<IdPSite> comparator = new IdPSite.Compare(req);
+610        
+611         if (config.getProvideList()) {
+612             sites = new TreeSet<IdPSite>(comparator);
+613         }
+614 
+615         if (searchName != null && !searchName.equals("")) {
+616             searchResults = new TreeSet<IdPSite>(comparator);
+617         }
+618 
+619         LOG.debug("Processing Idp Lookup for : " + providerId);
+620 
+621         //
+622         // Iterate over all the sitesets and if they know about the SP pass them to the plugins
+623         // and then add them too the list
+624         //
+625 
+626         PluginContext[] ctx = new PluginContext[plugins.size()];
+627         List<IdPSite> hintList = new ArrayList<IdPSite>();
+628         
+629         if (twoZeroProtocol) {
+630             setupReturnAddress(providerId, req);
+631         }
+632         //
+633         // By having siteLists and sites as parameters we only iterate over 
+634         // the metadata arrays once.
+635         //
+636         try {   
+637             for (IdPSiteSet metadataProvider:siteSets) {
+638                 
+639                 //
+640                 // Only do work if the SP makes sense
+641                 //
+642 
+643                 if (metadataProvider.containsSP(providerId) || !config.getLookupSp()) {
+644 
+645                     Collection <IdPSite> search = null;
+646 
+647                     if (null == sp) {
+648                         sp = metadataProvider.getEntity(providerId);
+649                     }
+650                     
+651                     if (searchResults != null) {
+652                         search = new TreeSet<IdPSite>(comparator);
+653                     }
+654 
+655                     Map <String, IdPSite> theseSites = metadataProvider.getIdPSites(searchName, config, search);
+656                     
+657                     //
+658                     // Consult the plugins
+659                     //
+660                     for (int i = 0; i < plugins.size(); i++) {
+661                             
+662                         Plugin plugin = plugins.get(i);
+663                         
+664                         if (searchResults == null) {
+665                             //
+666                             // This was a search
+667                             //
+668                             ctx[i] = plugin.lookup(req, 
+669                                                    res, 
+670                                                    metadataProvider.paramFor(plugin), 
+671                                                    theseSites, 
+672                                                    ctx[i], 
+673                                                    hintList);
+674                         } else {
+675                             ctx[i] = plugin.search(req, 
+676                                                    res, 
+677                                                    metadataProvider.paramFor(plugin), 
+678                                                    searchName, 
+679                                                    theseSites, 
+680                                                    ctx[i], 
+681                                                    searchResults, 
+682                                                    hintList);
+683                         }
+684                     }
+685                     
+686                     if (null == theseSites || theseSites.isEmpty()) {
+687                         continue;
+688                     }
+689                     
+690                     //
+691                         
+692                     // Accumulate any per-metadata provider information
+693                     // 
+694             
+695                     Collection<IdPSite> values = new TreeSet<IdPSite>(comparator);
+696                     if (null != theseSites) {
+697                         values.addAll(theseSites.values());
+698                     }
+699                     
+700                     if (siteLists != null) {
+701                         siteLists.add(new IdPSiteSetEntry(metadataProvider,values));
+702                     }
+703                             
+704                     if (sites != null) {
+705                         sites.addAll(values);
+706                     }
+707                     
+708                     if (searchResults != null) {
+709                         searchResults.addAll(search);
+710                     }
+711                 }
+712             }
+713             
+714             if (isPassive) {
+715                 //
+716                 // No GUI intervention.
+717                 //
+718                 if (0 != hintList.size()) {
+719                     //
+720                     // We found a candidate, hand it back
+721                     //
+722                     forwardRequest(req, res, hintList.get(0));
+723                 } else {
+724                     forwardRequest(req, res, null);
+725                 }   
+726                 return;
+727             }
+728             
+729             //
+730             // Now set up all the funky stuff that the JSP needs.  Firstly the protocol
+731             // specific parameters which will come back to us
+732             //
+733             
+734             if (twoZeroProtocol) {
+735                 //
+736                 // The return address was set up in setupReturnAddress
+737                 //
+738                 String returnString = (String) req.getAttribute(RETURN_ATTRIBUTE_NAME);
+739                 if (null == returnString || 0 == returnString.length()) {
+740                     throw new WayfException("Parameter " + RETURN_PARAM_NAME + " not supplied");
+741                 }
+742 
+743                 String returnId = getValue(req, RETURNID_PARAM_NAME);
+744                 if (null == returnId || 0 == returnId.length()) {
+745                     returnId = RETURNID_DEFAULT_VALUE;
+746                 }
+747                 //
+748                 // Return *means* something so we cannot use it as an attribute
+749                 //
+750                 req.setAttribute(RETURN_ATTRIBUTE_NAME, returnString);
+751                 req.setAttribute(RETURNID_PARAM_NAME, returnId);
+752                 req.setAttribute(ENTITYID_PARAM_NAME, providerId);
+753                 
+754             } else {
+755                 String target = getValue(req, TARGET_PARAM_NAME);
+756                 if (null == target || 0 == target.length()) {
+757                     throw new WayfException("Could not extract target from provided parameters");
+758                 }
+759                 req.setAttribute(SHIRE_PARAM_NAME, shire);
+760                 req.setAttribute(TARGET_PARAM_NAME, target);
+761                 req.setAttribute(PROVIDERID_PARAM_NAME, providerId);
+762                 //
+763                 // Time is in unix format
+764                 //
+765                 req.setAttribute("time", new Long(new Date().getTime() / 1000).toString()); 
+766                 
+767             }
+768             
+769             //
+770             // Setup the stuff that the GUI wants.  
+771             //
+772             setDisplayLanguage(sites, req);
+773             req.setAttribute("sites", sites);
+774             if (null != siteLists) {
+775                 for (IdPSiteSetEntry siteSetEntry:siteLists) {
+776                     setDisplayLanguage(siteSetEntry.getSites(), req);
+777                 }
+778             }
+779             
+780             req.setAttribute(PROVIDERID_OBJECT_PARAM_NAME, sp);
+781                 
+782             req.setAttribute("siteLists", siteLists);
+783             req.setAttribute("requestURL", req.getRequestURI().toString());
+784 
+785             if (searchResults != null) {
+786                 if (searchResults.size() != 0) {
+787                     setDisplayLanguage(searchResults, req);
+788                     req.setAttribute("searchresults", searchResults);
+789                 } else {
+790                     req.setAttribute("searchResultsEmpty", "true");
+791                 }
+792             }
+793 
+794             if (hintList.size() > 0) {
+795                 setDisplayLanguage(hintList, req);
+796                 req.setAttribute("cookieList", hintList);
+797             }
+798 
+799             LOG.debug("Displaying WAYF selection page.");
+800             RequestDispatcher rd = req.getRequestDispatcher(config.getJspFile());
+801 
+802             //
+803             // And off to the jsp
+804             //
+805             rd.forward(req, res);
+806         } catch (IOException ioe) {
+807             LOG.error("Problem displaying WAYF UI.\n" +  ioe.getMessage());
+808             throw new WayfException("Problem displaying WAYF UI", ioe);
+809         } catch (ServletException se) {
+810             LOG.error("Problem displaying WAYF UI.\n" +  se.getMessage());
+811             throw new WayfException("Problem displaying WAYF UI", se);
+812         }
+813     }
+814 
+815     /**
+816      * Prior to display we set the display language from the
+817      * browser. There is probably a proper way to do this using
+818      * jsp, but I want to keep the API between JSP and java the same 1.3->2.0
+819      * @param sites - the sites we need to impact
+820      * @param req - from whiuch we get the locale
+821      */
+822     private void setDisplayLanguage(Collection<IdPSite> sites, HttpServletRequest req) {
+823         
+824         if (null == sites) {
+825             return;
+826         }
+827         Locale locale = req.getLocale();
+828         if (null == locale) {
+829             Locale.getDefault();
+830         }
+831         String lang = locale.getLanguage();
+832 
+833         for (IdPSite site : sites) {
+834             site.setDisplayLanguage(lang);
+835         }
+836     }
+837 
+838 
+839     /**
+840      * Uses an HTTP Status 307 redirect to forward the user to the IdP or the SP.
+841      * 
+842      * @param req request under consideration
+843      * @param res response under construction
+844      * @param site The Idp
+845      * @throws WayfException if bad things happen.
+846      */
+847     public static void forwardRequest(HttpServletRequest req, HttpServletResponse res, IdPSite site)
+848                     throws WayfException {
+849 
+850         String shire = getValue(req, SHIRE_PARAM_NAME);
+851         String providerId = getSPId(req);
+852         boolean twoZeroProtocol = (shire == null);
+853 
+854         if (!twoZeroProtocol) {
+855             String handleService = site.getAddressForWAYF(); 
+856             if (handleService != null ) {
+857 
+858                 String target = getValue(req, TARGET_PARAM_NAME);
+859                 if (null == target || 0 == target.length()) {
+860                     throw new WayfException("Could not extract target from provided parameters");
+861                 }
+862 
+863                 LOG.info("Redirecting to selected Handle Service: " + handleService);
+864                 try {
+865                     StringBuffer buffer = new StringBuffer(handleService +  
+866                        "?" + TARGET_PARAM_NAME + "=");
+867                     buffer.append(URLEncoder.encode(target, "UTF-8"));
+868                     buffer.append("&" + SHIRE_PARAM_NAME + "=");
+869                     buffer.append(URLEncoder.encode(shire, "UTF-8"));
+870                     buffer.append("&" + PROVIDERID_PARAM_NAME + "=");
+871                     buffer.append(URLEncoder.encode(providerId, "UTF-8"));
+872                          
+873                     //
+874                     // Time is as per U**X
+875                     //
+876                     buffer.append("&" +  TIME_PARAM_NAME + "=");
+877                     buffer.append(new Long(new Date().getTime() / 1000).toString());
+878                     res.sendRedirect(buffer.toString());
+879                 } catch (IOException ioe) {
+880                     //
+881                     // That failed.  
+882                     //
+883                     throw new WayfException("Error forwarding to IdP: \n" + ioe.getMessage());
+884                 }
+885             } else {
+886                 String s = "Error finding to IdP: " + site.getDisplayName(req); 
+887                 LOG.error(s);
+888                 throw new WayfException(s);
+889             }
+890         } else {
+891             String returnUrl = (String) req.getAttribute(RETURN_ATTRIBUTE_NAME);
+892             
+893             if (null == returnUrl || 0 == returnUrl.length()) {
+894                 throw new WayfException("Could not find return parameter");
+895             }
+896             try {
+897                 returnUrl = URLDecoder.decode(returnUrl, "UTF-8");
+898             } catch (UnsupportedEncodingException e) {
+899                 throw new WayfException("Did not understand parameter ", e);
+900             }
+901             String redirect;
+902             if (site != null) {
+903                 StringBuffer buffer = new StringBuffer(returnUrl);
+904                 //
+905                 // If we were given anybody to lookup, construct the URL
+906                 //
+907                 String returnParam = getValue(req, RETURNID_PARAM_NAME);
+908                 if (null == returnParam || 0 == returnParam.length()) {
+909                     returnParam = RETURNID_DEFAULT_VALUE;
+910                 }              
+911                 //
+912                 // Do we add a '?' or a '&' for the parameters
+913                 //
+914 
+915                 if (returnUrl.indexOf('?') >= 0) {
+916                     //
+917                     // there is a parameter already.  Add a '&'
+918                     //
+919                     buffer.append("&" + returnParam + "=");
+920                 } else {
+921                     //
+922                     // No parameter.  Use ?
+923                     //
+924                     buffer.append("?" + returnParam + "=");
+925                 }
+926                 buffer.append(site.getName());
+927                 redirect =  buffer.toString();
+928             } else {
+929                 //
+930                 // Just send it back
+931                 //
+932                 redirect = returnUrl;
+933             }
+934             
+935             LOG.debug("Dispatching to " + redirect);
+936             
+937             try {
+938                 res.sendRedirect(redirect);
+939             } catch (IOException ioe) {
+940                 //
+941                 // That failed.  
+942                 //
+943                 throw new WayfException("Error forwarding back to Sp: \n" + ioe.getMessage());
+944             }         
+945         }
+946     }
+947 
+948     /**
+949      * Handles all "recoverable" errors in WAYF processing by logging the error and forwarding the user to an
+950      * appropriate error page.
+951      * 
+952      * @param req request under consideration
+953      * @param res response under construction
+954      * @param message - what so say
+955      */
+956     private void handleError(HttpServletRequest req, HttpServletResponse res, String message) {
+957 
+958         LOG.debug("Displaying WAYF error page.");
+959         req.setAttribute("errorText", message);
+960         req.setAttribute("requestURL", req.getRequestURI().toString());
+961         RequestDispatcher rd = req.getRequestDispatcher(config.getErrorJspFile());
+962 
+963         try {
+964             rd.forward(req, res);
+965         } catch (IOException ioe) {
+966             LOG.error("Problem trying to display WAYF error page: " + ioe.toString());
+967         } catch (ServletException se) {
+968             LOG.error("Problem trying to display WAYF error page: " + se.toString());
+969         }
+970     }
+971 
+972     /**
+973      * Gets the value for the parameter either from the parameter or from jsp.
+974      * @param req - the request.
+975      * @param name - the name of the parameter.
+976      * @return - result
+977      */
+978     private static String getValue(HttpServletRequest req, String name) {
+979 
+980         
+981         String value = req.getParameter(name); 
+982         if (value != null) {
+983             return value;
+984         }
+985         return (String) req.getAttribute(name);
+986     }
+987 
+988     private static String getSPId(HttpServletRequest req) throws WayfException {
+989 
+990         //
+991         // Try first with 2.0 version
+992         //
+993         String param = req.getParameter(ENTITYID_PARAM_NAME);
+994         if (param != null && !(param.length() == 0)) {
+995             return param;
+996         } 
+997         
+998         param = (String) req.getAttribute(ENTITYID_PARAM_NAME);
+999         if (param != null && !(param.length() == 0)) {
+1000             return param;
+1001         }       
+1002         //
+1003         // So Try with 1.3 version
+1004         //
+1005         param = req.getParameter(PROVIDERID_PARAM_NAME);
+1006         if (param != null && !(param.length() == 0)) {
+1007             return param;
+1008         } 
+1009         
+1010         param = (String) req.getAttribute(PROVIDERID_PARAM_NAME);
+1011         if (param != null && !(param.length() == 0)) {
+1012             return param;
+1013         } 
+1014         throw new WayfException("Could not locate SP identifier in parameters");
+1015     }   
+1016 }
 

-- cgit v1.1