View Javadoc

    1   /*
    2    * Copyright [2005] [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
    17  package edu.internet2.middleware.shibboleth.wayf;
    18
    19  import java.io.FileInputStream;
    20  import java.io.FileNotFoundException;
    21  import java.lang.reflect.Constructor;
    22  import java.util.ArrayList;
    23  import java.util.Hashtable;
    24  import java.util.Iterator;
    25  import java.util.List;
    26
    27  import javax.servlet.GenericServlet;
    28  import javax.servlet.ServletException;
    29  import javax.servlet.http.HttpServlet;
    30  import javax.servlet.http.HttpServletRequest;
    31  import javax.servlet.http.HttpServletResponse;
    32
    33  import org.opensaml.DefaultBootstrap;
    34  import org.opensaml.xml.parse.BasicParserPool;
    35  import org.opensaml.xml.util.DatatypeHelper;
    36  import org.slf4j.Logger;
    37  import org.slf4j.LoggerFactory;
    38  import org.w3c.dom.Document;
    39  import org.w3c.dom.Element;
    40  import org.w3c.dom.NodeList;
    41
    42  import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
    43  import edu.internet2.middleware.shibboleth.wayf.plugins.Plugin;
    44
    45  /**
    46   * A servlet implementation of the Shibboleth WAYF service. Allows a browser
    47   * user to select from among a group of origin sites. User selection is
    48   * optionally cached and the user is forwarded to the HandleService appropriate
    49   * to his selection.
    50   */
    51  public class WayfService extends HttpServlet {
    52
    53      /** Required constant for serializaton. */
    54      private static final long serialVersionUID = 5244503011625804940L;
    55
    56      /** Handle for outputting error and other messages. */
    57      private static final Logger LOG = LoggerFactory.getLogger(WayfService.class.getName());
    58
    59      /** Where to get the configuration. */
    60      private String wayfConfigFileLocation;
    61
    62      /** Logging service. */
    63      private LogbackLoggingService logService;
    64
    65      /** All the different Discovery Services we deal with. */
    66      private List <DiscoveryServiceHandler> discoveryServices = new ArrayList <DiscoveryServiceHandler>();
    67
    68      /**
    69       * Initialize the Discovery Service.
    70       *
    71       * @throws ServletException in the case of something bad happening
    72       *
    73       * @see GenericServlet#init()
    74       */
    75      public void init() throws ServletException {
    76
    77          super.init();
    78
    79          wayfConfigFileLocation = getServletContext().getInitParameter("WAYFConfigFileLocation");
    80          if (wayfConfigFileLocation == null) {
    81              wayfConfigFileLocation = getServletConfig().getInitParameter("WAYFConfigFileLocation");
    82          }
    83          if (wayfConfigFileLocation == null) {
    84              wayfConfigFileLocation = "/wayfconfig.xml";
    85          }
    86
    87
    88          try {
    89              //
    90              // Initialize logging
    91              //
    92              String wayfLogfile = getServletContext().getInitParameter("WAYFLogConfig");
    93              if (null == wayfLogfile) {
    94                  wayfLogfile = getServletConfig().getInitParameter("WAYFLogConfig");
    95              }
    96              long pollingFrequency = 1000*60*5;
    97
    98              String wayfLogfilePollFrequency = getServletContext().getInitParameter("WAYFLogConfigPollFrequency");
    99              if (null == wayfLogfilePollFrequency) {
    100                 wayfLogfilePollFrequency = getServletConfig().getInitParameter("WAYFLogConfigPollFrequency");
    101             }
    102             if(!DatatypeHelper.isEmpty(wayfLogfilePollFrequency)){
    103                 pollingFrequency = Long.parseLong(wayfLogfilePollFrequency);
    104             }
    105             if (wayfLogfile != null) {
    106                 logService = new LogbackLoggingService(wayfLogfile, pollingFrequency);
    107             }
    108
    109             LOG.info("Logging initiated");
    110
    111             //
    112             // Initialize OpenSAML 2 library
    113             //
    114             DefaultBootstrap.bootstrap();
    115
    116             BasicParserPool parser = new BasicParserPool();
    117             parser.setNamespaceAware(true);
    118             Document doc;
    119             try {
    120                 doc = parser.parse(new FileInputStream(wayfConfigFileLocation));
    121             } catch (FileNotFoundException e) {
    122                 LOG.error("Could not parse " + wayfConfigFileLocation, e);
    123                 throw new ShibbolethConfigurationException("Could not parse " + wayfConfigFileLocation, e);
    124             }
    125             NodeList itemElements = doc.getDocumentElement().getElementsByTagNameNS(XMLConstants.CONFIG_NS,
    126                                                                                     "Default");
    127
    128             HandlerConfig defaultHandlerConfig;
    129
    130             if (itemElements.getLength() == 1) {
    131
    132                 Element element = (Element) itemElements.item(0);
    133                 String attribute = element.getAttribute("location");
    134
    135                 if (attribute != null && !attribute.equals("")) {
    136
    137                     LOG.error("<Default> element cannot contain a location attribute");
    138                     throw new ShibbolethConfigurationException("<Default> element cannot contain a location attribute");
    139
    140                 }
    141
    142                 attribute = element.getAttribute("default");
    143
    144                 if (attribute != null && !attribute.equals("")) {
    145
    146                     LOG.error("<Default> element cannot contain a default attribute");
    147                     throw new ShibbolethConfigurationException("<Default> element cannot contain a default attribute");
    148
    149                 }
    150
    151                 itemElements = element.getElementsByTagName("Federation");
    152
    153                 if (itemElements.getLength() != 0) {
    154
    155                     LOG.error("<Default> element cannot contain <Federation> elements");
    156                     throw new ShibbolethConfigurationException("<Default> element cannot contain <Federation> elements");
    157
    158                 }
    159
    160                 defaultHandlerConfig = new HandlerConfig(element, new HandlerConfig());
    161
    162             } else if (itemElements.getLength() == 0) {
    163
    164                     defaultHandlerConfig = new HandlerConfig();
    165
    166             } else {
    167                     LOG.error("Must specify exactly one <Default> element");
    168                     throw new ShibbolethConfigurationException("Must specify exactly one <Default> element");
    169             }
    170
    171             //
    172             // Load metadata
    173             //
    174             Hashtable <String, IdPSiteSet> siteSets = new Hashtable <String, IdPSiteSet>();
    175
    176             itemElements = doc.getDocumentElement().getElementsByTagNameNS(XMLConstants.CONFIG_NS,
    177                             "MetadataProvider");
    178
    179             for (int i = 0; i < itemElements.getLength(); i++) {
    180
    181                 Element element = (Element) itemElements.item(i);
    182
    183                 IdPSiteSet siteset = new IdPSiteSet(element, parser, defaultHandlerConfig.getWarnOnBadBinding());
    184
    185                 siteSets.put(siteset.getIdentifier(), siteset);
    186             }
    187             if (siteSets.size() < 1) {
    188                 LOG.error("No Metadata Provider metadata loaded.");
    189                 throw new ShibbolethConfigurationException("Could not load SAML metadata.");
    190             }
    191             //
    192             // Load plugins
    193             //
    194
    195             Hashtable <String, Plugin> plugins = new Hashtable <String, Plugin>();
    196
    197             itemElements = doc.getDocumentElement().getElementsByTagNameNS(XMLConstants.CONFIG_NS,
    198                             "Plugin");
    199
    200             for (int i = 0; i < itemElements.getLength(); i++) {
    201
    202                 Plugin plugin;
    203
    204                 Element element = (Element) itemElements.item(i);
    205
    206                 String identifier = element.getAttribute("identifier");
    207
    208                 if (null == identifier || identifier.equals("")) {
    209                         LOG.error("Could not load plugin with no identifier");
    210                         continue;
    211                 }
    212
    213                 String className = element.getAttribute("type");
    214                 if (null == className || className.equals("")) {
    215                         LOG.error("Plugin " + identifier + " did not have a valid type");
    216                 }
    217                 //
    218                 // So try to get hold of the plugin
    219                 //
    220                 try {
    221                     Class<Plugin> pluginClass = (Class<Plugin>) Class.forName(className);
    222                     Class[] classParams = {Element.class};
    223                     Constructor<Plugin> pluginConstructor = pluginClass.getConstructor(classParams);
    224                     Object[] constructorParams = {element};
    225
    226                     plugin = pluginConstructor.newInstance(constructorParams);
    227
    228                 } catch (Exception e) {
    229                     LOG.error("Plugin " + identifier + " could not be loaded ", e);
    230                     continue;
    231                 }
    232
    233                 plugins.put(identifier, plugin);
    234             }
    235
    236
    237             //
    238             // Load service handlers
    239             //
    240             itemElements = doc.getDocumentElement().getElementsByTagNameNS(XMLConstants.CONFIG_NS,
    241                             "DiscoveryServiceHandler");
    242
    243             for (int i = 0; i < itemElements.getLength(); i++) {
    244
    245                 discoveryServices.add(new DiscoveryServiceHandler((Element)itemElements.item(i),
    246                                       siteSets,
    247                                       plugins,
    248                                       defaultHandlerConfig));
    249
    250             }
    251
    252         } catch (Exception e) {
    253         //
    254         // All other exceptions are from the parsing
    255         //
    256         if (LOG != null) {
    257                 LOG.error("Error parsing DS configuration file.", e);
    258         }
    259         throw new ServletException("Error parsing DS configuration file.", e);
    260     }
    261
    262     LOG.info("DS initialization completed.");
    263 }
    264
    265     /**
    266      * Handle an HTTP GET.  Just passes out to the appropriate handler.
    267      * @param req described the request.
    268      * @param res contains the response.
    269      * @see HttpServlet#doGet(HttpServletRequest, HttpServletResponse)
    270      */
    271     public void doGet(HttpServletRequest req, HttpServletResponse res) {
    272
    273         LOG.info("Handling DS request.");
    274         // Tell the browser not to cache the WAYF page
    275         res.setHeader("Cache-Control", "no-cache");
    276         res.setHeader("Pragma", "no-cache");
    277         res.setDateHeader("Expires", 0);
    278
    279         DiscoveryServiceHandler serviceHandler = lookupServiceHandler(req);
    280
    281         serviceHandler.doGet(req, res);
    282
    283     }
    284
    285     /**
    286      * Given a request (an HTTP Get) find the apropriate DiscoveryService (from the name).
    287      * @param req desribed the request
    288      * @return the apropriate DiscoveryService.
    289      */
    290     private DiscoveryServiceHandler lookupServiceHandler(HttpServletRequest req) {
    291
    292         Iterator<DiscoveryServiceHandler> it = discoveryServices.iterator();
    293         String requestURL = req.getRequestURL().toString();
    294         DiscoveryServiceHandler defaultHandler = null;
    295
    296         while (it.hasNext()) {
    297             DiscoveryServiceHandler handler = it.next();
    298
    299             if (requestURL.matches(handler.getLocation())) {
    300                 return handler;
    301             }
    302             if (defaultHandler == null || handler.isDefault()) {
    303                 defaultHandler = handler;
    304             }
    305         }
    306         LOG.warn("Could not find Discovery service Handler for " + requestURL);
    307         return defaultHandler;
    308     }
    309 }