1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 import javax.xml.namespace.QName;
33
34 import org.opensaml.DefaultBootstrap;
35 import org.opensaml.xml.Configuration;
36 import org.opensaml.xml.XMLObjectBuilderFactory;
37 import org.opensaml.xml.io.UnmarshallerFactory;
38 import org.opensaml.xml.parse.BasicParserPool;
39 import org.opensaml.xml.util.DatatypeHelper;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42 import org.w3c.dom.Document;
43 import org.w3c.dom.Element;
44 import org.w3c.dom.NodeList;
45
46 import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
47 import edu.internet2.middleware.shibboleth.wayf.idpdisco.Description;
48 import edu.internet2.middleware.shibboleth.wayf.idpdisco.DescriptionBuilder;
49 import edu.internet2.middleware.shibboleth.wayf.idpdisco.DescriptionUnmarshaller;
50 import edu.internet2.middleware.shibboleth.wayf.idpdisco.DiscoHints;
51 import edu.internet2.middleware.shibboleth.wayf.idpdisco.DiscoHintsBuilder;
52 import edu.internet2.middleware.shibboleth.wayf.idpdisco.DiscoHintsUnmarshaller;
53 import edu.internet2.middleware.shibboleth.wayf.idpdisco.DisplayName;
54 import edu.internet2.middleware.shibboleth.wayf.idpdisco.DisplayNameBuilder;
55 import edu.internet2.middleware.shibboleth.wayf.idpdisco.DisplayNameUnmarshaller;
56 import edu.internet2.middleware.shibboleth.wayf.idpdisco.DomainHint;
57 import edu.internet2.middleware.shibboleth.wayf.idpdisco.DomainHintBuilder;
58 import edu.internet2.middleware.shibboleth.wayf.idpdisco.DomainHintUnmarshaller;
59 import edu.internet2.middleware.shibboleth.wayf.idpdisco.GeolocationHint;
60 import edu.internet2.middleware.shibboleth.wayf.idpdisco.GeolocationHintBuilder;
61 import edu.internet2.middleware.shibboleth.wayf.idpdisco.GeolocationHintUnmarshaller;
62 import edu.internet2.middleware.shibboleth.wayf.idpdisco.IPHint;
63 import edu.internet2.middleware.shibboleth.wayf.idpdisco.IPHintBuilder;
64 import edu.internet2.middleware.shibboleth.wayf.idpdisco.IPHintUnmarshaller;
65 import edu.internet2.middleware.shibboleth.wayf.idpdisco.InformationURL;
66 import edu.internet2.middleware.shibboleth.wayf.idpdisco.InformationURLBuilder;
67 import edu.internet2.middleware.shibboleth.wayf.idpdisco.InformationURLUnmarshaller;
68 import edu.internet2.middleware.shibboleth.wayf.idpdisco.Logo;
69 import edu.internet2.middleware.shibboleth.wayf.idpdisco.LogoBuilder;
70 import edu.internet2.middleware.shibboleth.wayf.idpdisco.LogoUnmarshaller;
71 import edu.internet2.middleware.shibboleth.wayf.idpdisco.PrivacyStatementURL;
72 import edu.internet2.middleware.shibboleth.wayf.idpdisco.PrivacyStatementURLBuilder;
73 import edu.internet2.middleware.shibboleth.wayf.idpdisco.PrivacyStatementURLUnmarshaller;
74 import edu.internet2.middleware.shibboleth.wayf.idpdisco.UIInfo;
75 import edu.internet2.middleware.shibboleth.wayf.idpdisco.UIInfoBuilder;
76 import edu.internet2.middleware.shibboleth.wayf.idpdisco.UIInfoUnmarshaller;
77 import edu.internet2.middleware.shibboleth.wayf.plugins.Plugin;
78
79
80
81
82
83
84
85 public class WayfService extends HttpServlet {
86
87
88 private static final long serialVersionUID = 5244503011625804940L;
89
90
91 private static final Logger LOG = LoggerFactory.getLogger(WayfService.class.getName());
92
93
94 private String wayfConfigFileLocation;
95
96
97 private LogbackLoggingService logService;
98
99
100 private List <DiscoveryServiceHandler> discoveryServices = new ArrayList <DiscoveryServiceHandler>();
101
102
103
104
105 private void setupOtherSamlTypes(){
106 QName response;
107 UnmarshallerFactory uFactory = Configuration.getUnmarshallerFactory();
108 XMLObjectBuilderFactory bFactory = Configuration.getBuilderFactory();
109
110
111
112
113 response = new QName(UIInfo.MDUI_NS, Description.DEFAULT_ELEMENT_LOCAL_NAME);
114 uFactory.registerUnmarshaller(response, new DescriptionUnmarshaller());
115 bFactory.registerBuilder(response, new DescriptionBuilder());
116
117 response = new QName(UIInfo.MDUI_NS, DisplayName.DEFAULT_ELEMENT_LOCAL_NAME);
118 uFactory.registerUnmarshaller(response, new DisplayNameUnmarshaller());
119 bFactory.registerBuilder(response, new DisplayNameBuilder());
120
121 response = new QName(UIInfo.MDUI_NS, InformationURL.DEFAULT_ELEMENT_LOCAL_NAME);
122 uFactory.registerUnmarshaller(response, new InformationURLUnmarshaller());
123 bFactory.registerBuilder(response, new InformationURLBuilder());
124
125 response = new QName(UIInfo.MDUI_NS, Logo.DEFAULT_ELEMENT_LOCAL_NAME);
126 uFactory.registerUnmarshaller(response, new LogoUnmarshaller());
127 bFactory.registerBuilder(response, new LogoBuilder());
128
129 response = new QName(UIInfo.MDUI_NS, PrivacyStatementURL.DEFAULT_ELEMENT_LOCAL_NAME);
130 uFactory.registerUnmarshaller(response, new PrivacyStatementURLUnmarshaller());
131 bFactory.registerBuilder(response, new PrivacyStatementURLBuilder());
132
133 response = new QName(UIInfo.MDUI_NS, UIInfo.DEFAULT_ELEMENT_LOCAL_NAME);
134 uFactory.registerUnmarshaller(response, new UIInfoUnmarshaller());
135 bFactory.registerBuilder(response, new UIInfoBuilder());
136
137
138
139
140 response = new QName(DiscoHints.MDUI_NS, IPHint.DEFAULT_ELEMENT_LOCAL_NAME);
141 uFactory.registerUnmarshaller(response, new IPHintUnmarshaller());
142 bFactory.registerBuilder(response, new IPHintBuilder());
143
144 response = new QName(DiscoHints.MDUI_NS, GeolocationHint.DEFAULT_ELEMENT_LOCAL_NAME);
145 uFactory.registerUnmarshaller(response, new GeolocationHintUnmarshaller());
146 bFactory.registerBuilder(response, new GeolocationHintBuilder());
147
148 response = new QName(DiscoHints.MDUI_NS, DomainHint.DEFAULT_ELEMENT_LOCAL_NAME);
149 uFactory.registerUnmarshaller(response, new DomainHintUnmarshaller());
150 bFactory.registerBuilder(response, new DomainHintBuilder());
151
152 response = new QName(DiscoHints.MDUI_NS, DiscoHints.DEFAULT_ELEMENT_LOCAL_NAME);
153 uFactory.registerUnmarshaller(response, new DiscoHintsUnmarshaller());
154 bFactory.registerBuilder(response, new DiscoHintsBuilder());
155 }
156
157
158
159
160
161
162
163
164
165 public void init() throws ServletException {
166
167 String loadMetadataExts = null;
168
169 super.init();
170
171 wayfConfigFileLocation = getServletContext().getInitParameter("WAYFConfigFileLocation");
172 if (wayfConfigFileLocation == null) {
173 wayfConfigFileLocation = getServletConfig().getInitParameter("WAYFConfigFileLocation");
174 }
175 if (wayfConfigFileLocation == null) {
176 wayfConfigFileLocation = "/wayfconfig.xml";
177 }
178
179 loadMetadataExts = getServletContext().getInitParameter("loadMetadataExts");
180 if (loadMetadataExts == null) {
181 loadMetadataExts = getServletConfig().getInitParameter("loadMetadataExts");
182 }
183
184 try {
185
186
187
188 String wayfLogfile = getServletContext().getInitParameter("WAYFLogConfig");
189 if (null == wayfLogfile) {
190 wayfLogfile = getServletConfig().getInitParameter("WAYFLogConfig");
191 }
192 long pollingFrequency = 1000*60*5;
193
194 String wayfLogfilePollFrequency = getServletContext().getInitParameter("WAYFLogConfigPollFrequency");
195 if (null == wayfLogfilePollFrequency) {
196 wayfLogfilePollFrequency = getServletConfig().getInitParameter("WAYFLogConfigPollFrequency");
197 }
198 if(!DatatypeHelper.isEmpty(wayfLogfilePollFrequency)){
199 pollingFrequency = Long.parseLong(wayfLogfilePollFrequency);
200 }
201 if (wayfLogfile != null) {
202 logService = new LogbackLoggingService(wayfLogfile, pollingFrequency);
203 }
204
205 LOG.info("Logging initiated");
206
207
208
209
210 DefaultBootstrap.bootstrap();
211
212 BasicParserPool parser = new BasicParserPool();
213 parser.setNamespaceAware(true);
214 Document doc;
215 try {
216 doc = parser.parse(new FileInputStream(wayfConfigFileLocation));
217 } catch (FileNotFoundException e) {
218 LOG.error("Could not parse " + wayfConfigFileLocation, e);
219 throw new ShibbolethConfigurationException("Could not parse " + wayfConfigFileLocation, e);
220 }
221 NodeList itemElements = doc.getDocumentElement().getElementsByTagNameNS(XMLConstants.CONFIG_NS,
222 "Default");
223
224 HandlerConfig defaultHandlerConfig;
225
226 if (itemElements.getLength() == 1) {
227
228 Element element = (Element) itemElements.item(0);
229 String attribute = element.getAttribute("location");
230
231 if (attribute != null && !attribute.equals("")) {
232
233 LOG.error("<Default> element cannot contain a location attribute");
234 throw new ShibbolethConfigurationException("<Default> element cannot contain a location attribute");
235
236 }
237
238 attribute = element.getAttribute("default");
239
240 if (attribute != null && !attribute.equals("")) {
241
242 LOG.error("<Default> element cannot contain a default attribute");
243 throw new ShibbolethConfigurationException("<Default> element cannot contain a default attribute");
244
245 }
246
247 itemElements = element.getElementsByTagName("Federation");
248
249 if (itemElements.getLength() != 0) {
250
251 LOG.error("<Default> element cannot contain <Federation> elements");
252 throw new ShibbolethConfigurationException("<Default> element cannot contain <Federation> elements");
253
254 }
255
256 defaultHandlerConfig = new HandlerConfig(element, new HandlerConfig());
257
258 } else if (itemElements.getLength() == 0) {
259
260 defaultHandlerConfig = new HandlerConfig();
261
262 } else {
263 LOG.error("Must specify exactly one <Default> element");
264 throw new ShibbolethConfigurationException("Must specify exactly one <Default> element");
265 }
266
267
268
269
270 if (loadMetadataExts != null) {
271 LOG.debug("Setting up <UIInfo> and <DiscoHints> parsers - UNSUPPORTED OPERATION");
272 setupOtherSamlTypes();
273 }
274
275
276
277
278 Hashtable <String, IdPSiteSet> siteSets = new Hashtable <String, IdPSiteSet>();
279
280 itemElements = doc.getDocumentElement().getElementsByTagNameNS(XMLConstants.CONFIG_NS,
281 "MetadataProvider");
282
283 for (int i = 0; i < itemElements.getLength(); i++) {
284
285 Element element = (Element) itemElements.item(i);
286
287 IdPSiteSet siteset = new IdPSiteSet(element, parser, defaultHandlerConfig.getWarnOnBadBinding());
288
289 siteSets.put(siteset.getIdentifier(), siteset);
290 }
291 if (siteSets.size() < 1) {
292 LOG.error("No Metadata Provider metadata loaded.");
293 throw new ShibbolethConfigurationException("Could not load SAML metadata.");
294 }
295
296
297
298
299 Hashtable <String, Plugin> plugins = new Hashtable <String, Plugin>();
300
301 itemElements = doc.getDocumentElement().getElementsByTagNameNS(XMLConstants.CONFIG_NS,
302 "Plugin");
303
304 for (int i = 0; i < itemElements.getLength(); i++) {
305
306 Plugin plugin;
307
308 Element element = (Element) itemElements.item(i);
309
310 String identifier = element.getAttribute("identifier");
311
312 if (null == identifier || identifier.equals("")) {
313 LOG.error("Could not load plugin with no identifier");
314 continue;
315 }
316
317 String className = element.getAttribute("type");
318 if (null == className || className.equals("")) {
319 LOG.error("Plugin " + identifier + " did not have a valid type");
320 }
321
322
323
324 try {
325 Class<Plugin> pluginClass = (Class<Plugin>) Class.forName(className);
326 Class[] classParams = {Element.class};
327 Constructor<Plugin> pluginConstructor = pluginClass.getConstructor(classParams);
328 Object[] constructorParams = {element};
329
330 plugin = pluginConstructor.newInstance(constructorParams);
331
332 } catch (Exception e) {
333 LOG.error("Plugin " + identifier + " could not be loaded ", e);
334 continue;
335 }
336
337 plugins.put(identifier, plugin);
338 }
339
340
341
342
343
344 itemElements = doc.getDocumentElement().getElementsByTagNameNS(XMLConstants.CONFIG_NS,
345 "DiscoveryServiceHandler");
346
347 for (int i = 0; i < itemElements.getLength(); i++) {
348
349 discoveryServices.add(new DiscoveryServiceHandler((Element)itemElements.item(i),
350 siteSets,
351 plugins,
352 defaultHandlerConfig));
353
354 }
355
356 } catch (Exception e) {
357
358
359
360 if (LOG != null) {
361 LOG.error("Error parsing DS configuration file.", e);
362 }
363 throw new ServletException("Error parsing DS configuration file.", e);
364 }
365
366 LOG.info("DS initialization completed.");
367 }
368
369
370
371
372
373
374
375 public void doGet(HttpServletRequest req, HttpServletResponse res) {
376
377 LOG.info("Handling DS request.");
378
379 res.setHeader("Cache-Control", "no-cache");
380 res.setHeader("Pragma", "no-cache");
381 res.setDateHeader("Expires", 0);
382
383 DiscoveryServiceHandler serviceHandler = lookupServiceHandler(req);
384
385 serviceHandler.doGet(req, res);
386
387 }
388
389
390
391
392
393
394 private DiscoveryServiceHandler lookupServiceHandler(HttpServletRequest req) {
395
396 Iterator<DiscoveryServiceHandler> it = discoveryServices.iterator();
397 String requestURL = req.getRequestURL().toString();
398 DiscoveryServiceHandler defaultHandler = null;
399
400 while (it.hasNext()) {
401 DiscoveryServiceHandler handler = it.next();
402
403 if (requestURL.matches(handler.getLocation())) {
404 return handler;
405 }
406 if (defaultHandler == null || handler.isDefault()) {
407 defaultHandler = handler;
408 }
409 }
410 LOG.warn("Could not find Discovery service Handler for " + requestURL);
411 return defaultHandler;
412 }
413 }