<?xml version="1.0"?>
-<WayfConfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../schemas/wayfconfig.xsd"
- cacheType="COOKIES"
+<WayfConfig
+ xmlns="urn:mace:shibboleth:wayf:config:1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
cacheExpiration="604800"
supportContact="mailto:root@localhost"
logoLocation="images/logo.jpg">
+++ /dev/null
-/*
- * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package edu.internet2.middleware.shibboleth.common;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Enumeration;
-import java.util.Properties;
-import java.util.StringTokenizer;
-
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-import org.apache.commons.digester.Digester;
-import org.apache.log4j.Logger;
-import org.xml.sax.ErrorHandler;
-import org.xml.sax.InputSource;
-import org.xml.sax.SAXException;
-import org.xml.sax.SAXParseException;
-import org.xml.sax.XMLReader;
-
-/**
- * This class is a jakarta Digester style parser that will pull schemas from /WEB-INF/schemas, if they exist.
- *
- * @author Walter Hoehn wassa@columbia.edu
- */
-
-public class ServletDigester extends Digester {
-
- private static Logger log = Logger.getLogger(ServletDigester.class.getName());
-
- public ServletDigester() {
-
- super();
- setErrorHandler(new PassThruErrorHandler());
- }
-
- public ServletDigester(SAXParser parser) {
-
- super(parser);
- super.setErrorHandler(new PassThruErrorHandler());
- }
-
- public ServletDigester(XMLReader reader) {
-
- super(reader);
- super.setErrorHandler(new PassThruErrorHandler());
- }
-
- /**
- * @see org.xml.sax.EntityResolver#resolveEntity(String, String)
- */
- public InputSource resolveEntity(String publicId, String systemId) throws SAXException {
-
- log.debug("Resolving entity for System ID: " + systemId);
- if (systemId != null) {
- StringTokenizer tokenString = new StringTokenizer(systemId, "/");
- String xsdFile = "";
- while (tokenString.hasMoreTokens()) {
- xsdFile = tokenString.nextToken();
- }
- if (xsdFile.endsWith(".xsd")) {
- InputStream stream;
- try {
- stream = new ShibResource("/schemas/" + xsdFile, this.getClass()).getInputStream();
- } catch (IOException ioe) {
- log.error("Error loading schema: " + xsdFile + ": " + ioe);
- return null;
- }
- if (stream != null) { return new InputSource(stream); }
- }
- }
- return null;
-
- }
-
- /**
- * Return the SAXParser we will use to parse the input stream. If there is a problem creating the parser, return
- * <code>null</code>.
- */
- public SAXParser getParser() {
-
- // Return the parser we already created (if any)
- if (parser != null) { return (parser); }
-
- // Create and return a new parser
- synchronized (this) {
- try {
- if (factory == null) {
- factory = SAXParserFactory.newInstance();
- }
- factory.setNamespaceAware(namespaceAware);
- factory.setValidating(validating);
- if (validating) {
- factory.setFeature("http://xml.org/sax/features/namespaces", true);
- factory.setFeature("http://xml.org/sax/features/validation", true);
- factory.setFeature("http://apache.org/xml/features/validation/schema", true);
- factory.setFeature("http://apache.org/xml/features/validation/schema-full-checking", true);
- }
- parser = factory.newSAXParser();
- if (validating) {
-
- Properties schemaProps = new Properties();
- schemaProps.load(new ShibResource("/conf/schemas.properties", this.getClass()).getInputStream());
- String schemaLocations = "";
- Enumeration schemas = schemaProps.propertyNames();
- while (schemas.hasMoreElements()) {
- String ns = (String) schemas.nextElement();
- schemaLocations += ns + " " + schemaProps.getProperty(ns) + " ";
- }
- log.debug("Overriding schema locations for the following namespace: " + schemaLocations);
- parser.setProperty("http://apache.org/xml/properties/schema/external-schemaLocation",
- schemaLocations);
- }
- return (parser);
- } catch (Exception e) {
- log.error("Error during Digester initialization", e);
- return (null);
- }
- }
-
- }
-
- /**
- * Sax <code>ErrorHandler</code> that passes all errors up as new exceptions.
- */
-
- public class PassThruErrorHandler implements ErrorHandler {
-
- /**
- * @see org.xml.sax.ErrorHandler#error(SAXParseException)
- */
- public void error(SAXParseException arg0) throws SAXException {
-
- throw new SAXException("Error parsing xml file: " + arg0);
- }
-
- /**
- * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException)
- */
- public void fatalError(SAXParseException arg0) throws SAXException {
-
- throw new SAXException("Error parsing xml file: " + arg0);
- }
-
- /**
- * @see org.xml.sax.ErrorHandler#warning(SAXParseException)
- */
- public void warning(SAXParseException arg0) throws SAXException {
-
- throw new SAXException("Error parsing xml file: " + arg0);
- }
-
- }
-}
+++ /dev/null
-/*
- * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package edu.internet2.middleware.shibboleth.wayf;
-
-import javax.servlet.http.Cookie;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Implementation of <code>WayfCache</code> that uses Http Cookies to cache user selections.
- *
- * @author Walter Hoehn wassa@columbia.edu
- */
-public class CookieWayfCache extends WayfCacheBase implements WayfCache {
-
- private int expiration;
- private String domain;
-
- /**
- * Constructs a <code>CookieWayfCache</code>
- *
- * @param expiration
- * Cache validity period in seconds
- * @param domain
- * Domain to which the cookie will be released
- */
- public CookieWayfCache(int expiration, String domain) {
-
- this.expiration = expiration;
- if (domain != null && domain != "") {
- this.domain = domain;
- }
- }
-
- /**
- * @see WayfCache#addHsToCache(HttpServletRequest)
- */
- public void addHsToCache(String handleService, HttpServletRequest req, HttpServletResponse res) {
-
- Cookie cacheCookie = new Cookie("edu.internet2.middleware.shibboleth.wayf.selectedHandleService", handleService);
- configureCookie(cacheCookie);
- res.addCookie(cacheCookie);
- }
-
- /**
- * @see WayfCache#deleteHsFromCache(HttpServletRequest)
- */
- public void deleteHsFromCache(HttpServletRequest req, HttpServletResponse res) {
-
- Cookie[] cookies = req.getCookies();
- if (cookies == null) { return; }
- for (int i = 0; i < cookies.length; i++) {
- if (cookies[i].getName().equals("edu.internet2.middleware.shibboleth.wayf.selectedHandleService")) {
- configureCookie(cookies[i]);
- cookies[i].setMaxAge(0);
- res.addCookie(cookies[i]);
- }
- }
- }
-
- /**
- * @see WayfCache#getCachedHS(HttpServletRequest)
- */
- public String getCachedHS(HttpServletRequest req) {
-
- Cookie[] cookies = req.getCookies();
- if (cookies != null) {
- for (int i = 0; i < cookies.length; i++) {
- if (cookies[i].getName().equals("edu.internet2.middleware.shibboleth.wayf.selectedHandleService")) { return cookies[i]
- .getValue(); }
- }
- }
- return null;
- }
-
- private void configureCookie(Cookie cookie) {
-
- cookie.setComment("Used to cache selection of a user's Shibboleth Handle Service");
- cookie.setPath("/");
-
- if (expiration > 0) {
- cookie.setMaxAge(expiration);
- }
- if (domain != null && domain != "") {
- cookie.setDomain(domain);
- }
- }
-
-}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package edu.internet2.middleware.shibboleth.wayf;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Implementaton of the <code>WayfCache</code> interface that does no cacheing of user selections.
- *
- * @author Walter Hoehn wassa@columbia.edu
- */
-
-public class NullWayfCache implements WayfCache {
-
- /**
- * @see WayfCache#addHsToCache(HttpServletRequest)
- */
- public void addHsToCache(String handleService, HttpServletRequest req, HttpServletResponse res) {
-
- // don't do anything
- }
-
- /**
- * @see WayfCache#deleteHsFromCache(HttpServletRequest)
- */
- public void deleteHsFromCache(HttpServletRequest req, HttpServletResponse res) {
-
- // don't do anything
- }
-
- /**
- * @see WayfCache#getCachedHS(HttpServletRequest)
- */
- public String getCachedHS(HttpServletRequest req) {
-
- return null;
- }
-
- /**
- * @see WayfCache#hasCachedHS(HttpServletRequest)
- */
- public boolean hasCachedHS(HttpServletRequest req) {
-
- return false;
- }
-
-}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package edu.internet2.middleware.shibboleth.wayf;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.bouncycastle.util.encoders.Base64;
+
+/**
+ * Implementation of the <code>_saml_idp </code> cookie.
+ *
+ * Note that any SamlIdPCookie is only valid for as long as the reqest/response
+ * parameters provided to getIdPCookie remain valid.
+ *
+ * @author Rod Widdowson
+ *
+ */
+public class SamlIdPCookie {
+
+ private static final String COOKIE_NAME = "_saml_idp";
+
+ private final HttpServletRequest req;
+ private final HttpServletResponse res;
+ private final String domain;
+ private final ArrayList /*<String>*/ idPList = new ArrayList/*<String>*/();
+
+ /**
+ * Constructs a <code>SamlIdPCookie</code> from the provided string (which is the raw data
+ *
+ * @param codedData
+ * the information read from the cookie
+ * @param domain - if non null the domain for any *created* cookie.
+ */
+ private SamlIdPCookie(String codedData, HttpServletRequest req, HttpServletResponse res, String domain) {
+
+ this.req = req;
+ this.res = res;
+ this.domain = domain;
+
+ int start;
+ int end;
+
+ if (codedData == null || codedData.equals("")) {
+ return;
+ }
+ //
+ // Because there are spaces in the data the cookie may be returned enclosed in quotes
+ //
+ if (codedData.charAt(0) == '"' && codedData.charAt(codedData.length()-1) == '"') {
+ codedData= codedData.substring(1,codedData.length()-1);
+ }
+
+
+ start = 0;
+ end = codedData.indexOf(' ', start);
+ while (end > 0) {
+ String value = codedData.substring(start, end);
+ start = end + 1;
+ end = codedData.indexOf(' ', start);
+ if (!value.equals("")) {
+ idPList.add(new String(Base64.decode(value)));
+ }
+ }
+ if (start < codedData.length()) {
+ String value = codedData.substring(start);
+ if (!value.equals("")) {
+ idPList.add(new String(Base64.decode(value)));
+ }
+ }
+ }
+ /**
+ * Create a SamlCookie with no data inside.
+ * @param domain - if non null, the domain of the new cookie
+ *
+ */
+ public SamlIdPCookie(HttpServletRequest req, HttpServletResponse res, String domain) {
+ this.req = req;
+ this.res = res;
+ this.domain = domain;
+ }
+
+ /**
+ * Add the specified Shibboleth IdP Name to the cookie list or move to
+ * the front and then write it back.
+ *
+ * We always add to the front (and remove from wherever it was)
+ *
+ * @param idPName - The name to be added
+ * @param expiration - The expiration of the cookie or zero if it is to be unchanged
+ */
+ public void addIdPName(String idPName, int expiration) {
+
+ idPList.remove(idPName);
+ idPList.add(0, idPName);
+
+ writeCookie(expiration);
+ }
+
+ /**
+ * Return an iterator over the list of IdPNames
+ * @param which
+ * @return
+ */
+
+ public Iterator/*<String>*/ iterator() {
+
+ return idPList.iterator();
+ }
+
+ /**
+ * Delete the <b>entire<\b> cookie contents
+ */
+
+ public static void deleteCookie(HttpServletRequest req, HttpServletResponse res) {
+ Cookie cookie = getCookie(req);
+
+ if (cookie == null) {
+ return;
+ }
+ cookie.setMaxAge(0);
+ res.addCookie(cookie);
+ }
+
+ /**
+ * Load up the cookie and convert it into a SamlIdPCookie. If there is no
+ * underlying cookie return a null one.
+ * @param domain - if this is set then any <b>created</b> cookies are set to this domain
+ */
+
+ public static SamlIdPCookie getIdPCookie(HttpServletRequest req, HttpServletResponse res, String domain) {
+ Cookie cookie = getCookie(req);
+
+ if (cookie == null) {
+ return new SamlIdPCookie(req, res, domain);
+ } else {
+ return new SamlIdPCookie(cookie.getValue(), req, res, domain);
+ }
+ }
+
+ /**
+ * Remove origin from the cachedata and write it back.
+ * @param origin
+ */
+
+ public void deleteIdPName(String origin) {
+ idPList.remove(origin);
+ writeCookie(0);
+ }
+
+ private void writeCookie(int expiration)
+ {
+ Cookie cookie = getCookie(req);
+
+ if (idPList.size() == 0) {
+ //
+ // Nothing to write, so delete the cookie
+ //
+ cookie.setMaxAge(0);
+ res.addCookie(cookie);
+ return;
+ }
+
+ //
+ // Otherwise encode up the cookie
+ //
+
+ StringBuffer buffer = new StringBuffer();
+ Iterator /*<String>*/ it = idPList.iterator();
+
+ while (it.hasNext()) {
+ String next = (String) it.next();
+ String what = new String(Base64.encode(next.getBytes()));
+ buffer.append(what).append(' ');
+ }
+
+ String value = buffer.toString();
+
+ if (cookie == null) {
+ cookie = new Cookie(COOKIE_NAME, value);
+ } else {
+ cookie.setValue(value);
+ }
+ cookie.setComment("Used to cache selection of a user's Shibboleth IdP");
+ cookie.setPath("/");
+
+ if (expiration > 0) {
+ cookie.setMaxAge(expiration);
+ }
+ if (domain != null && domain != "") {
+ cookie.setDomain(domain);
+ }
+ res.addCookie(cookie);
+
+ }
+
+ private static Cookie getCookie(HttpServletRequest req) {
+
+ Cookie[] cookies = req.getCookies();
+ if (cookies != null) {
+ for (int i = 0; i < cookies.length; i++) {
+ if (cookies[i].getName().equals(COOKIE_NAME)) {
+ return cookies[i];
+ }
+ }
+ }
+ return null;
+ }
+}
+++ /dev/null
-/*
- * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package edu.internet2.middleware.shibboleth.wayf;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import javax.servlet.http.HttpSession;
-
-/**
- * Implementation of <code>WayfCache</code> that uses Java Servlet Sessions to cache user selections.
- *
- * @author Walter Hoehn wassa@columbia.edu
- */
-
-public class SessionWayfCache extends WayfCacheBase implements WayfCache {
-
- private int expiration;
-
- /**
- * @param expiration
- * The time in seconds between requests at which point this cache entry will be invalidated.
- */
- public SessionWayfCache(int expiration) {
-
- if (expiration == 0) {
- this.expiration = 7200;
- } else {
- this.expiration = expiration;
- }
- }
-
- /**
- * @see WayfCache#addHsToCache(HttpServletRequest)
- */
- public void addHsToCache(String handleService, HttpServletRequest req, HttpServletResponse res) {
-
- HttpSession session = req.getSession(true);
- session.setMaxInactiveInterval(expiration);
- session.setAttribute("selectedHandleService", handleService);
- }
-
- /**
- * @see WayfCache#deleteHsFromCache(HttpServletRequest)
- */
- public void deleteHsFromCache(HttpServletRequest req, HttpServletResponse res) {
-
- HttpSession session = req.getSession(false);
- if (session != null) {
- session.removeAttribute("selectedHandleService");
- }
- }
-
- /**
- * @see WayfCache#getCachedHS(HttpServletRequest)
- */
- public String getCachedHS(HttpServletRequest req) {
-
- HttpSession session = req.getSession(false);
- if (session == null) { return null; }
- return (String) session.getAttribute("selectedHandleService");
- }
-}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package edu.internet2.middleware.shibboleth.wayf;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-/**
- * Defines a method for cacheing user selections regarding which shibboleth Handle Service should be used.
- *
- * @author Walter Hoehn wassa@columbia.edu
- */
-
-public interface WayfCache {
-
- /**
- * Add the specified Shibboleth Handle Service to the cache.
- */
- public void addHsToCache(String handleService, HttpServletRequest req, HttpServletResponse res);
-
- /**
- * Delete the Shibboleth Handle Service assoctiated with the current requester from the cache.
- */
- public void deleteHsFromCache(HttpServletRequest req, HttpServletResponse res);
-
- /**
- * Returns boolean indicator as to whether the current requester has a Handle Service entry in the cache.
- */
- public boolean hasCachedHS(HttpServletRequest req);
-
- /**
- * Retrieves the Handle Service associated with the current requester. Returns null if there is none currently
- * associated.
- */
- public String getCachedHS(HttpServletRequest req);
-
-}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package edu.internet2.middleware.shibboleth.wayf;
-
-import javax.servlet.http.HttpServletRequest;
-
-/**
- * Shared implementation code for <code>WayfCache</code>.
- *
- * @author Walter Hoehn wassa@columbia.edu
- */
-public abstract class WayfCacheBase implements WayfCache {
-
- /**
- * @see WayfCache#getCachedHS(HttpServletRequest)
- */
- public boolean hasCachedHS(HttpServletRequest req) {
-
- if (getCachedHS(req) == null) {
- return false;
- } else {
- return true;
- }
- }
-
-}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package edu.internet2.middleware.shibboleth.wayf;
-
-import org.apache.log4j.Logger;
-
-/**
- * Factory for creating instances of <code>WayfCache</code> based on the state of the <code>WayfConfig</code>.
- *
- * @author Walter Hoehn wassa@columbia.edu
- */
-public class WayfCacheFactory {
-
- private static Logger log = Logger.getLogger(WayfCacheFactory.class.getName());
-
- public static WayfCache getInstance(String cacheType, WayfCacheOptions options) {
-
- if (cacheType.equals("NONE")) {
- return new NullWayfCache();
- } else if (cacheType.equals("SESSION")) {
- return new SessionWayfCache(options.getExpiration());
- } else if (cacheType.equals("COOKIES")) {
- return new CookieWayfCache(options.getExpiration(), options.getDomain());
- } else {
- log.warn("Invalid Cache type specified: running with cache type NONE.");
- return new NullWayfCache();
- }
- }
-
- public static WayfCache getInstance(String cacheType) {
-
- return getInstance(cacheType, new WayfCacheOptions());
- }
-
-}
\ No newline at end of file
+++ /dev/null
-/*
- * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package edu.internet2.middleware.shibboleth.wayf;
-
-/**
- * Runtime configuration bundle that is passed to a <code>WayfCacheFactory</code>.
- *
- * @author Walter Hoehn wassa@columbia.edu
- */
-public class WayfCacheOptions {
-
- private int expiration;
- private String domain;
-
- /**
- * Returns the domain.
- *
- * @return String
- */
- public String getDomain() {
-
- return domain;
- }
-
- /**
- * Returns the expiration.
- *
- * @return int
- */
- public int getExpiration() {
-
- return expiration;
- }
-
- /**
- * Sets the domain.
- *
- * @param domain
- * The domain to set
- */
- public void setDomain(String domain) {
-
- this.domain = domain;
- }
-
- /**
- * Sets the expiration.
- *
- * @param expiration
- * The expiration to set
- */
- public void setExpiration(int expiration) {
-
- this.expiration = expiration;
- }
-
-}
import java.util.HashSet;
import org.apache.log4j.Logger;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import edu.internet2.middleware.shibboleth.common.ShibbolethConfigurationException;
/**
* Class used by the WAYF service to determine runtime options Most of the fields of this class should have reasonable
private HashSet ignoredForMatch = new HashSet();
private int cacheExpiration;
private String cacheDomain;
- private String cacheType = "COOKIES";
-
- public WayfConfig() {
+ private String getValue(Element element, String what) throws ShibbolethConfigurationException
+ {
+ NodeList list = element.getElementsByTagName(what);
+
+ if (list.getLength() > 0) {
+ if (list.getLength() > 1) {
+ throw new ShibbolethConfigurationException("More than one <" + what + "/> element");
+ }
+
+ return list.item(0).getTextContent();
+ }
+ return null;
+ }
+
+ /**
+ *
+ * Parse the Supplied XML element into a new WayfConfig Object
+ *
+ */
+
+ public WayfConfig(Element config) throws ShibbolethConfigurationException {
+
+ if (!config.getTagName().equals("WayfConfig")) {
+
+ throw new ShibbolethConfigurationException(
+ "Unexpected configuration data. <WayfConfig/> is needed.");
+ }
+
+ log.debug("Loading global configuration properties.");
+
+ String raw = config.getAttribute("cacheDomain");
+
+ if ((raw != null) && (raw != "")) {
+ setCacheDomain(raw);
+ }
+
+ raw = config.getAttribute("cacheExpiration");
+ if ((raw != null) && (raw != "")) {
+
+ try {
+
+ setCacheExpiration(Integer.parseInt(raw));
+ } catch (NumberFormatException ex) {
+
+ throw new ShibbolethConfigurationException("Invalid CacheExpiration value - " + raw, ex);
+ }
+ }
+
+ raw = config.getAttribute("logoLocation");
+ if ((raw != null) && (raw != "")) {
+
+ setLogoLocation(raw);
+ }
+
+ raw = config.getAttribute("supportContact");
+ if ((raw != null) && (raw != "")) {
+
+ setSupportContact(raw);
+ }
+
+ raw = getValue(config, "HelpText");
+
+ if ((raw != null) && (raw != "")) {
+
+ setHelpText(raw);
+ }
+
+ raw = getValue(config, "SearchResultEmptyText");
+
+ if ((raw != null) && (raw != "")) {
+
+ setSearchResultEmptyText(raw);
+ }
+
+ NodeList list = config.getElementsByTagName("SearchIgnore");
+
+ for (int i = 0; i < list.getLength(); i++ ) {
+
+ NodeList inner = ((Element) list.item(i)).getElementsByTagName("IgnoreText");
+
+ for(int j = 0; j < inner.getLength(); j++) {
+
+ addIgnoredForMatch(inner.item(j).getTextContent());
+ }
+ }
+
+ }
+
+ public WayfConfig()
+ {
super();
}
ignoredForMatch.add(s.toLowerCase());
}
- public String getCacheType() {
-
- return cacheType;
- }
-
- public void setCacheType(String cache) {
-
- if (cache.toUpperCase().equals("NONE") || cache.toUpperCase().equals("SESSION")
- || cache.toUpperCase().equals("COOKIES")) {
- this.cacheType = cache.toUpperCase();
- } else {
- log.warn("Cache type :" + cache + ": not recognized, using default.");
- }
- }
-
/**
* Returns the cacheDomain.
*
this.cacheExpiration = cacheExpiration;
}
-}
\ No newline at end of file
+}
+++ /dev/null
-/*
- * Copyright [2005] [University Corporation for Advanced Internet Development, Inc.]
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package edu.internet2.middleware.shibboleth.wayf;
-
-import javax.xml.parsers.SAXParser;
-
-import org.xml.sax.XMLReader;
-
-import edu.internet2.middleware.shibboleth.common.ServletDigester;
-
-/**
- * This class is a jakarta Digester style parser for the WAYF configuration file. It should populate the WayfConfig
- * object during WAYF initilization. NOTE: It is assumed that the mutators of this class will only be called by a single
- * thread during servlet initilization only (NOT thread safe)
- *
- * @author Walter Hoehn wassa@columbia.edu
- */
-
-public class WayfConfigDigester extends ServletDigester {
-
- protected String wayfConfigClass = "edu.internet2.middleware.shibboleth.wayf.WayfConfig";
- private boolean configured = false;
-
- public WayfConfigDigester() {
-
- super();
- configure();
- }
-
- public WayfConfigDigester(SAXParser parser) {
-
- super(parser);
- configure();
- }
-
- public WayfConfigDigester(XMLReader reader) {
-
- super(reader);
- configure();
- }
-
- /**
- * @see Digester#configure()
- */
- protected void configure() {
-
- if (configured == true) { return; }
- addObjectCreate("WayfConfig", wayfConfigClass);
- addSetProperties("WayfConfig");
- addCallMethod("WayfConfig/HelpText", "setHelpText", 0);
- addCallMethod("WayfConfig/SearchResultEmptyText", "setSearchResultEmptyText", 0);
- addCallMethod("WayfConfig/SearchIgnore/IgnoreText", "addIgnoredForMatch", 0);
-
- configured = true;
-
- }
-
-}
\ No newline at end of file
package edu.internet2.middleware.shibboleth.wayf;
import java.io.IOException;
-import java.io.InputStream;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.Date;
+import java.util.Iterator;
+import javax.servlet.GenericServlet;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletResponse;
import org.apache.log4j.Logger;
-import org.xml.sax.SAXException;
+import org.w3c.dom.Document;
-import edu.internet2.middleware.shibboleth.common.ShibResource;
import edu.internet2.middleware.shibboleth.common.ShibResource.ResourceNotAvailableException;
import edu.internet2.middleware.shibboleth.metadata.Metadata;
-import edu.internet2.middleware.shibboleth.metadata.provider.XMLMetadata;
import edu.internet2.middleware.shibboleth.metadata.MetadataException;
+import edu.internet2.middleware.shibboleth.metadata.provider.XMLMetadata;
+import edu.internet2.middleware.shibboleth.xml.Parser;
/**
* A servlet implementation of the Shibboleth WAYF service. Allows a browser
private Metadata metadata;
- private WayfCacheOptions wSessionOptions = new WayfCacheOptions();
-
- private WayfCacheOptions wPermOptions = new WayfCacheOptions();
-
private static Logger log = Logger.getLogger(WayfService.class.getName());
/**
throw new ServletException(e);
}
- // Setup Cacheing options
- wSessionOptions.setDomain(config.getCacheDomain());
- wPermOptions.setDomain(config.getCacheDomain());
- wPermOptions.setExpiration(config.getCacheExpiration());
-
initViewConfig();
log.info("WAYF initialization completed.");
}
* Populates WayfConfig from file contents.
*/
private void configure() throws UnavailableException {
-
try {
- InputStream is = new ShibResource(wayfConfigFileLocation, this.getClass()).getInputStream();
- WayfConfigDigester digester = new WayfConfigDigester();
- digester.setValidating(true);
- config = (WayfConfig) digester.parse(is);
-
- } catch (SAXException se) {
- log.fatal("Error parsing WAYF configuration file.", se);
+ Document doc = Parser.loadDom(wayfConfigFileLocation, true);
+ config = new WayfConfig(doc.getDocumentElement());
+ } catch (IOException e) {
+ log.fatal("Error Loading WAYF configuration file.", e);
+ throw new UnavailableException("Error parsing WAYF configuration file.");
+ } catch (Exception e) {
+ // All other exceptions are from the parsing
+ log.fatal("Error parsing WAYF configuration file.", e);
throw new UnavailableException("Error parsing WAYF configuration file.");
- } catch (IOException ioe) {
- log.fatal("Error reading WAYF configuration file.", ioe);
- throw new UnavailableException("Error reading WAYF configuration file.");
}
}
try {
if (requestType.equals("deleteFromCache")) {
log.debug("Deleting saved HS from cache");
- WayfCacheFactory.getInstance(config.getCacheType(), wPermOptions).deleteHsFromCache(req, res);
+ SamlIdPCookie.deleteCookie(req, res);
handleLookup(req, res);
- } else if (WayfCacheFactory.getInstance(config.getCacheType()).hasCachedHS(req)) {
- forwardToHS(req, res, WayfCacheFactory.getInstance(config.getCacheType()).getCachedHS(req));
- } else if (requestType.equals("search")) {
+ return;
+ }
+
+ SamlIdPCookie cookie;
+ if (req.getParameter("nolookup") == null) {
+ cookie = SamlIdPCookie.getIdPCookie(req, res, config.getCacheDomain());
+ } else {
+ // For the test case, do not do a cache lookup, start as empty
+ cookie = new SamlIdPCookie(req, res, config.getCacheDomain());
+ }
+
+ if (requestType.equals("search")) {
handleSearch(req, res);
} else if (requestType.equals("selection")) {
- handleSelection(req, res);
+ String origin = req.getParameter("origin");
+ log.debug("Processing handle selection: " + origin);
+ if (origin == null) {
+ handleLookup(req, res);
+ } else {
+ if ((req.getParameter("cache") != null)) {
+ if (req.getParameter("cache").equalsIgnoreCase("session")) {
+ cookie.addIdPName(origin, 0);
+ } else if (req.getParameter("cache").equalsIgnoreCase("perm")) {
+ cookie.addIdPName(origin, config.getCacheExpiration());
+ }
+ }
+ redirectToIdP(req, res, origin, cookie);
+ }
} else {
- handleLookup(req, res);
+ // Try for a cache hit
+ String idPName = null;
+ Iterator it = cookie.iterator();
+
+ //
+ // The cached data may contain several IdPs, some of which we do
+ // not know about
+ // so iterate down until we find one we do know about
+ //
+ while (it.hasNext()) {
+ idPName = (String) it.next();
+ if (metadata.lookup(idPName) != null) {
+ break;
+ }
+ }
+
+ if (idPName != null) {
+ //
+ // move the name to the head of the list, preserving the
+ // cache expiration
+ //
+ cookie.addIdPName(idPName, 0);
+ redirectToIdP(req, res, idPName, cookie);
+ } else {
+ handleLookup(req, res);
+ }
}
} catch (WayfException we) {
handleError(req, res, we);
}
req.setAttribute("time", new Long(new Date().getTime() / 1000).toString()); // Unix
- // Time
+ // Time
req.setAttribute("requestURL", req.getRequestURI().toString());
log.debug("Displaying WAYF selection page.");
/**
* Registers a user's HS selection and forwards appropriately
*/
- private void handleSelection(HttpServletRequest req, HttpServletResponse res) throws WayfException {
-
- log.debug("Processing handle selection: " + req.getParameter("origin"));
- String handleService = null;
+ private void redirectToIdP(HttpServletRequest req, HttpServletResponse res, String idPName, SamlIdPCookie cookie)
+ throws WayfException {
+ String idPSSOEndPoint = null;
try {
//
// If we have had a refresh between then and now the following will
// fail
//
- handleService = metadata.lookup(req.getParameter("origin")).getIDPSSODescriptor(
+ idPSSOEndPoint = metadata.lookup(idPName).getIDPSSODescriptor(
edu.internet2.middleware.shibboleth.common.XML.SHIB_NS).getSingleSignOnServiceManager()
.getDefaultEndpoint().getLocation();
} catch (Exception ex) {
- log.error("Error dispatching to IdP", ex);
+ //
+ // remove this entry (only) from the cache
+ //
+ cookie.deleteIdPName(idPName);
+ log.error("Error dispatching to IdP: ", ex);
}
- if (handleService == null) {
- handleLookup(req, res);
- } else {
- if ((req.getParameter("cache") != null)) {
- if (req.getParameter("cache").equalsIgnoreCase("session")) {
- WayfCacheFactory.getInstance(config.getCacheType(), wSessionOptions).addHsToCache(handleService,
- req, res);
- } else if (req.getParameter("cache").equalsIgnoreCase("perm")) {
- WayfCacheFactory.getInstance(config.getCacheType(), wPermOptions).addHsToCache(handleService, req,
- res);
+ if (idPSSOEndPoint != null) {
+ log.info("Redirecting to SSO at selected IdP: " + idPSSOEndPoint);
+ try {
+ StringBuffer buffer = new StringBuffer(idPSSOEndPoint).append("?target=");
+ buffer.append(URLEncoder.encode(getTarget(req), "UTF-8")).append("&shire=");
+ buffer.append(URLEncoder.encode(getSHIRE(req), "UTF-8"));
+ String providerId = getProviderId(req);
+ log.debug("WALTER: (" + providerId + ").");
+ if (providerId != null) {
+ buffer.append("&providerId=").append(URLEncoder.encode(getProviderId(req), "UTF-8"));
}
+ buffer.append("&time=").append(new Long(new Date().getTime() / 1000).toString()); // Unix
+ // Time
+ res.sendRedirect(buffer.toString());
+ } catch (IOException ioe) {
+ //
+ // remove this entry (only) from the cache
+ //
+ cookie.deleteIdPName(idPName);
+ throw new WayfException("Error forwarding to IdP SSO endpoint: " + ioe.toString());
}
- forwardToHS(req, res, handleService);
- }
-
- }
-
- /**
- * Uses an HTTP Status 307 redirect to forward the user the HS.
- *
- * @param handleService The URL of the Shiboleth HS.
- */
- private void forwardToHS(HttpServletRequest req, HttpServletResponse res, String handleService)
- throws WayfException {
-
- log.info("Redirecting to selected Handle Service");
- try {
- StringBuffer buffer = new StringBuffer(handleService + "?target="
- + URLEncoder.encode(getTarget(req), "UTF-8") + "&shire="
- + URLEncoder.encode(getSHIRE(req), "UTF-8"));
- String providerId = getProviderId(req);
- log.debug("WALTER: (" + providerId + ").");
- if (providerId != null) {
- buffer.append("&providerId=" + URLEncoder.encode(getProviderId(req), "UTF-8"));
- }
- buffer.append("&time=" + new Long(new Date().getTime() / 1000).toString()); // Unix
- // Time
- res.sendRedirect(buffer.toString());
- } catch (IOException ioe) {
- throw new WayfException("Error forwarding to HS: " + ioe.toString());
+ } else {
+ //
+ // We
+ handleLookup(req, res);
}
-
}
/**
"urn:oasis:names:tc:SAML:1.0:protocol",
"urn:mace:shibboleth:namemapper:1.0",
"urn:mace:shibboleth:idp:config:1.0",
+ "urn:mace:shibboleth:wayf:config:1.0",
"urn:mace:shibboleth:arp:1.0",
"urn:mace:shibboleth:resolver:1.0",
"urn:oasis:names:tc:SAML:2.0:metadata",
<?xml version="1.0" encoding="UTF-8"?>
<!-- Schema for the Shibboleth WAYF Service configuration file - Walter Hoehn - 06/14/2002 -->
-<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="unqualified">
+<xs:schema
+ targetNamespace="urn:mace:shibboleth:wayf:config:1.0"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ elementFormDefault="qualified">
<xs:element name="WayfConfig" >
<xs:complexType>
<xs:sequence>
</xs:sequence>
<xs:attribute name="supportContact" type="xs:string" use="optional"/>
<xs:attribute name="logoLocation" type="xs:string" use="optional"/>
- <xs:attribute name="cacheType" type="xs:string" use="optional"/>
<xs:attribute name="cacheExpiration" type="xs:string" use="optional"/>
<xs:attribute name="cacheDomain" type="xs:string" use="optional"/>
</xs:complexType>