View Javadoc

1   /*
2    * Copyright 2009 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.idp.util;
18  
19  import java.util.UUID;
20  
21  import javax.servlet.ServletContext;
22  import javax.servlet.http.Cookie;
23  import javax.servlet.http.HttpServletRequest;
24  import javax.servlet.http.HttpServletResponse;
25  
26  import org.opensaml.saml2.metadata.EntityDescriptor;
27  import org.opensaml.saml2.metadata.provider.MetadataProviderException;
28  import org.opensaml.util.URLBuilder;
29  import org.opensaml.util.storage.StorageService;
30  import org.opensaml.xml.util.DatatypeHelper;
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  
34  import sun.security.action.GetLongAction;
35  
36  import edu.internet2.middleware.shibboleth.common.attribute.filtering.AttributeFilteringEngine;
37  import edu.internet2.middleware.shibboleth.common.attribute.provider.SAML1AttributeAuthority;
38  import edu.internet2.middleware.shibboleth.common.attribute.provider.SAML2AttributeAuthority;
39  import edu.internet2.middleware.shibboleth.common.attribute.resolver.AttributeResolver;
40  import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfigurationManager;
41  import edu.internet2.middleware.shibboleth.common.relyingparty.provider.SAMLMDRelyingPartyConfigurationManager;
42  import edu.internet2.middleware.shibboleth.common.session.SessionManager;
43  import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
44  import edu.internet2.middleware.shibboleth.idp.authn.LoginContextEntry;
45  import edu.internet2.middleware.shibboleth.idp.profile.IdPProfileHandlerManager;
46  import edu.internet2.middleware.shibboleth.idp.session.Session;
47  
48  /** A helper class that provides access to internal state from Servlets and hence also JSPs. */
49  public class HttpServletHelper {
50  
51      /** Name of the context initialization parameter that stores the domain to use for all cookies. */
52      public static final String COOKIE_DOMAIN_PARAM = "cookieDomain";
53  
54      /** Name of the cookie containing the IdP session ID: {@value} . */
55      public static final String IDP_SESSION_COOKIE = "_idp_session";
56  
57      /** Name of the key to the current authentication login context: {@value} . */
58      public static final String LOGIN_CTX_KEY_NAME = "_idp_authn_lc_key";
59  
60      /** {@link ServletContext} parameter name bearing the ID of the {@link AttributeFilteringEngine} service: {@value} . */
61      public static final String ATTRIBUTE_FILTER_ENGINE_SID_CTX_PARAM = "AttributeFilterEngineId";
62  
63      /** {@link ServletContext} parameter name bearing the ID of the {@link AttributeResolver} service: {@value} . */
64      public static final String ATTRIBUTE_RESOLVER_SID_CTX_PARAM = "AttributeResolverId";
65  
66      /**
67       * {@link ServletContext} parameter name bearing the name of the {@link StorageService} partition into which
68       * {@link LoginContext}s are stored: {@value} .
69       */
70      public static final String LOGIN_CTX_PARTITION_CTX_PARAM = "loginContextPartitionName";
71  
72      /** {@link ServletContext} parameter name bearing the ID of the {@link IdPProfileHandlerManager} service: {@value} . */
73      public static final String PROFILE_HANDLER_MNGR_SID_CTX_PARAM = "ProfileHandlerMngrId";
74  
75      /**
76       * {@link ServletContext} parameter name bearing the ID of the {@link RelyingPartyConfigurationManager} service: * *
77       * * {@value} .
78       */
79      public static final String RP_CONFIG_MNGR_SID_CTX_PARAM = "RelyingPartyConfigurationManagerId";
80  
81      /** {@link ServletContext} parameter name bearing the ID of the {@link SAML1AttributeAuthority} service: {@value} . */
82      public static final String SAML1_AA_SID_CTX_PARAM = "SAML1AttributeAuthorityId";
83  
84      /** {@link ServletContext} parameter name bearing the ID of the {@link SAML2AttributeAuthority} service: {@value} . */
85      public static final String SAML2_AA_SID_CTX_PARAM = "SAML2AttributeAuthorityId";
86  
87      /** {@link ServletContext} parameter name bearing the ID of the {@link SessionManager} service: {@value} . */
88      public static final String SESSION_MNGR_SID_CTX_PARAM = "SessionManagerId";
89  
90      /** {@link ServletContext} parameter name bearing the ID of the {@link SAML1AttributeAuthority} service: {@value} . */
91      public static final String STORAGE_SERVICE_SID_CTX_PARAM = "StorageServiceId";
92  
93      /** Default ID by which the {@link AttributeFilteringEngine} is know within the Servlet context: {@value} . */
94      public static final String DEFAULT_ATTRIBUTE_FILTER_ENGINE_SID = "shibboleth.AttributeFilterEngine";
95  
96      /** Default ID by which the {@link AttributeResolver} is know within the Servlet context: {@value} . */
97      public static final String DEFAULT_ATTRIBUTE_RESOLVER_SID = "shibboleth.AttributeResolver";
98  
99      /** Default name for the {@link StorageService} partition which holds {@link LoginContext}s: {@value} . */
100     public static final String DEFAULT_LOGIN_CTX_PARITION = "loginContexts";
101 
102     /** Default ID by which the {@link IdPProfileHandlerManager} is know within the Servlet context: {@value} . */
103     public static final String DEFAULT_PROFILE_HANDLER_MNGR_SID = "shibboleth.HandlerManager";
104 
105     /** Default ID by which the {@link RelyingPartyConfigurationManager} is know within the Servlet context: {@value} . */
106     public static final String DEFAULT_RP_CONFIG_MNGR_SID = "shibboleth.RelyingPartyConfigurationManager";
107 
108     /** Default ID by which the {@link SAML1AttributeAuthority} is know within the Servlet context: {@value} . */
109     public static final String DEFAULT_SAML1_AA_SID = "shibboleth.SAML1AttributeAuthority";
110 
111     /** Default ID by which the {@link SAML2AttributeAuthority} is know within the Servlet context: {@value} . */
112     public static final String DEFAULT_SAML2_AA_SID = "shibboleth.SAML2AttributeAuthority";
113 
114     /** Default ID by which the {@link SessionManager} is know within the Servlet context: {@value} . */
115     public static final String DEFAULT_SESSION_MNGR_SID = "shibboleth.SessionManager";
116 
117     /** Default ID by which the {@link StorageService} is know within the Servlet context: {@value} . */
118     public static final String DEFAULT_STORAGE_SERVICE_SID = "shibboleth.StorageService";
119 
120     /** Class logger. */
121     private static final Logger log = LoggerFactory.getLogger(HttpServletHelper.class);
122 
123     /**
124      * Binds a {@link LoginContext} to the current request.
125      * 
126      * @param loginContext login context to be bound
127      * @param httpRequest current HTTP request
128      * 
129      * @deprecated
130      */
131     public static void bindLoginContext(LoginContext loginContext, HttpServletRequest httpRequest) {
132         if (httpRequest == null) {
133             throw new IllegalArgumentException("HTTP request may not be null");
134         }
135         httpRequest.setAttribute(LOGIN_CTX_KEY_NAME, loginContext);
136     }
137 
138     /**
139      * Binds a {@link LoginContext} to the issuer of the current request. The binding is done by creating a random UUID,
140      * placing that in a cookie in the request, and storing the context in to the storage service under that key.
141      * 
142      * @param loginContext the login context to be bound
143      * @param storageService the storage service which will hold the context
144      * @param context the Servlet context
145      * @param httpRequest the current HTTP request
146      * @param httpResponse the current HTTP response
147      */
148     public static void bindLoginContext(LoginContext loginContext, StorageService storageService,
149             ServletContext context, HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
150         if (storageService == null) {
151             throw new IllegalArgumentException("Storage service may not be null");
152         }
153         if (httpRequest == null) {
154             throw new IllegalArgumentException("HTTP request may not be null");
155         }
156         if (loginContext == null) {
157             return;
158         }
159 
160         String parition = getContextParam(context, LOGIN_CTX_PARTITION_CTX_PARAM, DEFAULT_LOGIN_CTX_PARITION);
161 
162         String contextKey = UUID.randomUUID().toString();
163         while (storageService.contains(parition, contextKey)) {
164             contextKey = UUID.randomUUID().toString();
165         }
166 
167         LoginContextEntry entry = new LoginContextEntry(loginContext, 1800000);
168         log.debug("Storing LoginContext to StorageService partition {}, key {}", parition, contextKey);
169         storageService.put(parition, contextKey, entry);
170 
171         String cookieDomain = getCookieDomain(context);
172 
173         Cookie contextKeyCookie = new Cookie(LOGIN_CTX_KEY_NAME, contextKey);
174         contextKeyCookie.setVersion(1);
175         if (cookieDomain != null) {
176             contextKeyCookie.setDomain(cookieDomain);
177         }
178         contextKeyCookie.setPath("".equals(httpRequest.getContextPath()) ? "/" : httpRequest.getContextPath());
179         contextKeyCookie.setSecure(httpRequest.isSecure());
180         httpResponse.addCookie(contextKeyCookie);
181         
182         httpRequest.setAttribute(LOGIN_CTX_KEY_NAME, loginContext);
183     }
184 
185     /**
186      * Gets the domain to use for all cookies.
187      * 
188      * @param context web application context
189      * 
190      * @return domain to use for all cookies
191      */
192     public static String getCookieDomain(ServletContext context) {
193         return context.getInitParameter(COOKIE_DOMAIN_PARAM);
194     }
195 
196     /**
197      * Gets the {@link AttributeFilteringEngine} service bound to the Servlet context.
198      * 
199      * @param context the Servlet context
200      * 
201      * @return the service or null if there is no such service bound to the context
202      */
203     public static AttributeFilteringEngine<?> getAttributeFilterEnginer(ServletContext context) {
204         return getAttributeFilterEnginer(context,
205                 getContextParam(context, ATTRIBUTE_FILTER_ENGINE_SID_CTX_PARAM, DEFAULT_ATTRIBUTE_FILTER_ENGINE_SID));
206     }
207 
208     /**
209      * Gets the {@link AttributeFilteringEngine} bound to the Servlet context.
210      * 
211      * @param context the Servlet context
212      * @param serviceId the ID under which the service bound
213      * 
214      * @return the service or null if there is no such service bound to the context
215      */
216     public static AttributeFilteringEngine<?> getAttributeFilterEnginer(ServletContext context, String serviceId) {
217         return (AttributeFilteringEngine<?>) context.getAttribute(serviceId);
218     }
219 
220     /**
221      * Gets the {@link AttributeResolver} service bound to the Servlet context.
222      * 
223      * @param context the Servlet context
224      * 
225      * @return the service or null if there is no such service bound to the context
226      */
227     public static AttributeResolver<?> getAttributeResolver(ServletContext context) {
228         return getAttributeResolver(context,
229                 getContextParam(context, ATTRIBUTE_RESOLVER_SID_CTX_PARAM, DEFAULT_ATTRIBUTE_RESOLVER_SID));
230     }
231 
232     /**
233      * Gets the {@link AttributeResolver} bound to the Servlet context.
234      * 
235      * @param context the Servlet context
236      * @param serviceId the ID under which the service bound
237      * 
238      * @return the service or null if there is no such service bound to the context
239      */
240     public static AttributeResolver<?> getAttributeResolver(ServletContext context, String serviceId) {
241         return (AttributeResolver<?>) context.getAttribute(serviceId);
242     }
243 
244     /**
245      * Gets a value for a given context parameter. If no value is present the default value is used.
246      * 
247      * @param context the Servlet context
248      * @param name name of the context parameter
249      * @param defaultValue default value of the parameter
250      * 
251      * @return the value of the context parameter or the default value if the parameter is not set or does not contain a
252      *         value
253      */
254     public static String getContextParam(ServletContext context, String name, String defaultValue) {
255         String value = DatatypeHelper.safeTrimOrNullString(context.getInitParameter(name));
256         if (value == null) {
257             value = defaultValue;
258         }
259         return value;
260     }
261 
262     /**
263      * Gets the first {@link Cookie} whose name matches the given name.
264      * 
265      * @param cookieName the cookie name
266      * @param httpRequest HTTP request from which the cookie should be extracted
267      * 
268      * @return the cookie or null if no cookie with that name was given
269      */
270     public static Cookie getCookie(HttpServletRequest httpRequest, String cookieName) {
271         Cookie[] requestCookies = httpRequest.getCookies();
272         if (requestCookies != null) {
273             for (Cookie requestCookie : requestCookies) {
274                 if (requestCookie != null && DatatypeHelper.safeEquals(requestCookie.getName(), cookieName)) {
275                     return requestCookie;
276                 }
277             }
278         }
279 
280         return null;
281     }
282 
283     /**
284      * Gets the login context from the current request. The login context is only in this location while the request is
285      * being transferred from the authentication engine back to the profile handler.
286      * 
287      * This method only works during the first hand-off from the authentication engine to the login handler. Afterwords
288      * you must use {@link #getLoginContext(StorageService, ServletContext, HttpServletRequest)}.
289      * 
290      * @param httpRequest current HTTP request
291      * 
292      * @return the login context or null if no login context is bound to the request
293      */
294     public static LoginContext getLoginContext(HttpServletRequest httpRequest) {
295         return (LoginContext) httpRequest.getAttribute(LOGIN_CTX_KEY_NAME);
296     }
297 
298     /**
299      * Gets the {@link LoginContext} for the user issuing the HTTP request. Note, login contexts are only available
300      * during the authentication process.
301      * 
302      * @param context the Servlet context
303      * @param storageService storage service to use when retrieving the login context
304      * @param httpRequest current HTTP request
305      * 
306      * @return the login context or null if none is available
307      */
308     public static LoginContext getLoginContext(StorageService storageService, ServletContext context,
309             HttpServletRequest httpRequest) {
310         if (storageService == null) {
311             throw new IllegalArgumentException("Storage service may not be null");
312         }
313         if (context == null) {
314             throw new IllegalArgumentException("Servlet context may not be null");
315         }
316         if (httpRequest == null) {
317             throw new IllegalArgumentException("HTTP request may not be null");
318         }
319 
320         Cookie loginContextKeyCookie = getCookie(httpRequest, LOGIN_CTX_KEY_NAME);
321         if (loginContextKeyCookie == null) {
322             log.debug("LoginContext key cookie was not present in request");
323             return null;
324         }
325 
326         String loginContextKey = DatatypeHelper.safeTrimOrNullString(loginContextKeyCookie.getValue());
327         if (loginContextKey == null) {
328             log.warn("Corrupted LoginContext Key cookie, it did not contain a value");
329         }
330 
331         String partition = getContextParam(context, LOGIN_CTX_PARTITION_CTX_PARAM, DEFAULT_LOGIN_CTX_PARITION);
332         log.trace("Looking up LoginContext with key {} from StorageService parition: {}", loginContextKey, partition);
333         LoginContextEntry entry = (LoginContextEntry) storageService.get(partition, loginContextKey);
334         if (entry != null) {
335             if (entry.isExpired()) {
336                 log.debug("LoginContext found but it was expired");
337             } else {
338                 log.trace("Retrieved LoginContext with key {} from StorageService parition: {}", loginContextKey,
339                         partition);
340                 return entry.getLoginContext();
341             }
342         } else {
343             log.debug("No login context in storage service");
344         }
345 
346         return null;
347     }
348 
349     /**
350      * Gets the {@link IdPProfileHandlerManager} service bound to the Servlet context.
351      * 
352      * @param context the Servlet context
353      * 
354      * @return the service or null if there is no such service bound to the context
355      */
356     public static IdPProfileHandlerManager getProfileHandlerManager(ServletContext context) {
357         return getProfileHandlerManager(context,
358                 getContextParam(context, PROFILE_HANDLER_MNGR_SID_CTX_PARAM, DEFAULT_PROFILE_HANDLER_MNGR_SID));
359     }
360 
361     /**
362      * Gets the {@link IdPProfileHandlerManager} bound to the Servlet context.
363      * 
364      * @param context the Servlet context
365      * @param serviceId the ID under which the service bound
366      * 
367      * @return the service or null if there is no such service bound to the context
368      */
369     public static IdPProfileHandlerManager getProfileHandlerManager(ServletContext context, String serviceId) {
370         return (IdPProfileHandlerManager) context.getAttribute(serviceId);
371     }
372 
373     /**
374      * Gets the {@link RelyingPartyConfigurationManager} service bound to the Servlet context.
375      * 
376      * @param context the Servlet context
377      * 
378      * @return the service or null if there is no such service bound to the context
379      */
380     public static RelyingPartyConfigurationManager getRelyingPartyConfigurationManager(ServletContext context) {
381         return getRelyingPartyConfigurationManager(context,
382                 getContextParam(context, RP_CONFIG_MNGR_SID_CTX_PARAM, DEFAULT_RP_CONFIG_MNGR_SID));
383     }
384 
385     /**
386      * Gets the {@link RelyingPartyConfigurationManager} bound to the Servlet context.
387      * 
388      * @param context the Servlet context
389      * @param serviceId the ID under which the service bound
390      * 
391      * @return the service or null if there is no such service bound to the context
392      */
393     public static RelyingPartyConfigurationManager getRelyingPartyConfigurationManager(ServletContext context,
394             String serviceId) {
395         return (RelyingPartyConfigurationManager) context.getAttribute(serviceId);
396     }
397 
398     /**
399      * Gets the {@link RelyingPartyConfigurationManager} service bound to the Servlet context.
400      * 
401      * @param context the Servlet context
402      * 
403      * @return the service or null if there is no such service bound to the context
404      * 
405      * @deprecated use {@link #getRelyingPartyConfigurationManager(ServletContext)}
406      */
407     public static RelyingPartyConfigurationManager getRelyingPartyConfirmationManager(ServletContext context) {
408         return getRelyingPartyConfirmationManager(context,
409                 getContextParam(context, RP_CONFIG_MNGR_SID_CTX_PARAM, DEFAULT_RP_CONFIG_MNGR_SID));
410     }
411 
412     /**
413      * Gets the {@link RelyingPartyConfigurationManager} bound to the Servlet context.
414      * 
415      * @param context the Servlet context
416      * @param serviceId the ID under which the service bound
417      * 
418      * @return the service or null if there is no such service bound to the context
419      * 
420      * @deprecated use {@link #getRelyingPartyConfigurationManager(ServletContext, String)}
421 
422      */
423     public static RelyingPartyConfigurationManager getRelyingPartyConfirmationManager(ServletContext context,
424             String serviceId) {
425         return (RelyingPartyConfigurationManager) context.getAttribute(serviceId);
426     }
427 
428     /**
429      * Gets the metatdata for a given relying party.
430      * 
431      * @param relyingPartyEntityId the ID of the relying party
432      * @param rpConfigMngr relying party configuration manager
433      * 
434      * @return the metadata for the relying party or null if no SAML metadata exists for the given relying party
435      */
436     public static EntityDescriptor getRelyingPartyMetadata(String relyingPartyEntityId,
437             RelyingPartyConfigurationManager rpConfigMngr) {
438         if (rpConfigMngr instanceof SAMLMDRelyingPartyConfigurationManager) {
439             SAMLMDRelyingPartyConfigurationManager samlRpConfigMngr = (SAMLMDRelyingPartyConfigurationManager) rpConfigMngr;
440             try {
441                 return samlRpConfigMngr.getMetadataProvider().getEntityDescriptor(relyingPartyEntityId);
442             } catch (MetadataProviderException e) {
443 
444             }
445         }
446 
447         return null;
448     }
449 
450     /**
451      * Gets the {@link SAML1AttributeAuthority} service bound to the Servlet context.
452      * 
453      * @param context the Servlet context
454      * 
455      * @return the service or null if there is no such service bound to the context
456      */
457     public static SAML1AttributeAuthority getSAML1AttributeAuthority(ServletContext context) {
458         return getSAML1AttributeAuthority(context,
459                 getContextParam(context, SAML1_AA_SID_CTX_PARAM, DEFAULT_SAML1_AA_SID));
460     }
461 
462     /**
463      * Gets the {@link SAML1AttributeAuthority} bound to the Servlet context.
464      * 
465      * @param context the Servlet context
466      * @param serviceId the ID under which the service bound
467      * 
468      * @return the service or null if there is no such service bound to the context
469      */
470     public static SAML1AttributeAuthority getSAML1AttributeAuthority(ServletContext context, String serviceId) {
471         return (SAML1AttributeAuthority) context.getAttribute(serviceId);
472     }
473 
474     /**
475      * Gets the {@link SAML2AttributeAuthority} service bound to the Servlet context.
476      * 
477      * @param context the Servlet context
478      * 
479      * @return the service or null if there is no such service bound to the context
480      */
481     public static SAML2AttributeAuthority getSAML2AttributeAuthority(ServletContext context) {
482         return getSAML2AttributeAuthority(context,
483                 getContextParam(context, SAML2_AA_SID_CTX_PARAM, DEFAULT_SAML2_AA_SID));
484     }
485 
486     /**
487      * Gets the {@link SAML2AttributeAuthority} bound to the Servlet context.
488      * 
489      * @param context the Servlet context
490      * @param serviceId the ID under which the service bound
491      * 
492      * @return the service or null if there is no such service bound to the context
493      */
494     public static SAML2AttributeAuthority getSAML2AttributeAuthority(ServletContext context, String serviceId) {
495         return (SAML2AttributeAuthority) context.getAttribute(serviceId);
496     }
497 
498     /**
499      * Gets the {@link SessionManager} service bound to the Servlet context.
500      * 
501      * @param context the Servlet context
502      * 
503      * @return the service or null if there is no such service bound to the context
504      */
505     public static SessionManager<Session> getSessionManager(ServletContext context) {
506         return getSessionManager(context,
507                 getContextParam(context, SESSION_MNGR_SID_CTX_PARAM, DEFAULT_SESSION_MNGR_SID));
508     }
509 
510     /**
511      * Gets the {@link SessionManager} bound to the Servlet context.
512      * 
513      * @param context the Servlet context
514      * @param serviceId the ID under which the service bound
515      * 
516      * @return the service or null if there is no such service bound to the context
517      */
518     public static SessionManager<Session> getSessionManager(ServletContext context, String serviceId) {
519         return (SessionManager<Session>) context.getAttribute(serviceId);
520     }
521 
522     /**
523      * Gets the {@link StorageService} service bound to the Servlet context.
524      * 
525      * @param context the Servlet context
526      * 
527      * @return the service or null if there is no such service bound to the context
528      */
529     public static StorageService<?, ?> getStorageService(ServletContext context) {
530         return getStorageService(context,
531                 getContextParam(context, STORAGE_SERVICE_SID_CTX_PARAM, DEFAULT_STORAGE_SERVICE_SID));
532     }
533 
534     /**
535      * Gets the {@link StorageService} bound to the Servlet context.
536      * 
537      * @param context the Servlet context
538      * @param serviceId the ID under which the service bound
539      * 
540      * @return the service or null if there is no such service bound to the context
541      */
542     public static StorageService<?, ?> getStorageService(ServletContext context, String serviceId) {
543         return (StorageService<?, ?>) context.getAttribute(serviceId);
544     }
545 
546     /**
547      * Gets the user session from the request. Retrieving the session in this manner does NOT update the last activity
548      * time of the session.
549      * 
550      * @param httpRequest current request
551      * 
552      * @return the users session, if one exists
553      */
554     public static Session getUserSession(HttpServletRequest httpRequest) {
555         return (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
556     }
557 
558     /**
559      * Unbinds a {@link LoginContext} from the current request. The unbinding results in the destruction of the
560      * associated context key cookie and removes the context from the storage service.
561      * 
562      * @param storageService storage service holding the context
563      * @param context the Servlet context
564      * @param httpRequest current HTTP request
565      * @param httpResponse current HTTP response
566      * 
567      * @return the login context that was unbound or null if there was no bound context
568      */
569     public static LoginContext unbindLoginContext(StorageService storageService, ServletContext context,
570             HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
571         log.debug("Unbinding LoginContext");
572         if (storageService == null) {
573             throw new IllegalArgumentException("Storage service may not be null");
574         }
575         if (context == null) {
576             throw new IllegalArgumentException("Servlet context may not be null");
577         }
578         if (httpRequest == null) {
579             throw new IllegalArgumentException("HTTP request may not be null");
580         }
581         if (httpResponse == null) {
582             throw new IllegalArgumentException("HTTP request may not be null");
583         }
584 
585         Cookie loginContextKeyCookie = getCookie(httpRequest, LOGIN_CTX_KEY_NAME);
586         if (loginContextKeyCookie == null) {
587             log.debug("No LoginContext cookie available, no unbinding necessary.");
588             return null;
589         }
590 
591         String loginContextKey = DatatypeHelper.safeTrimOrNullString(loginContextKeyCookie.getValue());
592         if (loginContextKey == null) {
593             log.warn("Corrupted LoginContext Key cookie, it did not contain a value");
594             return null;
595         }
596 
597         log.debug("Expiring LoginContext cookie");
598         loginContextKeyCookie.setMaxAge(0);
599         loginContextKeyCookie.setPath("".equals(httpRequest.getContextPath()) ? "/" : httpRequest.getContextPath());
600         loginContextKeyCookie.setVersion(1);
601         httpResponse.addCookie(loginContextKeyCookie);
602 
603         String storageServicePartition = getContextParam(context, LOGIN_CTX_PARTITION_CTX_PARAM,
604                 DEFAULT_LOGIN_CTX_PARITION);
605 
606         log.debug("Removing LoginContext, with key {}, from StorageService partition {}", loginContextKey,
607                 storageServicePartition);
608         LoginContextEntry entry = (LoginContextEntry) storageService.remove(storageServicePartition, loginContextKey);
609         if (entry != null && !entry.isExpired()) {
610             return entry.getLoginContext();
611         }
612 
613         return null;
614     }
615 
616     /**
617      * Builds a URL, up to and including the servlet context path. URL does not include a trailing "/".
618      * 
619      * @param httpRequest httpRequest made to the servlet in question
620      * 
621      * @return URL builder containing the scheme, server name, server port, and context path
622      */
623     public static URLBuilder getServletContextUrl(HttpServletRequest httpRequest) {
624         URLBuilder urlBuilder = new URLBuilder();
625         urlBuilder.setScheme(httpRequest.getScheme());
626         urlBuilder.setHost(httpRequest.getServerName());
627         urlBuilder.setPort(httpRequest.getServerPort());
628         urlBuilder.setPath(httpRequest.getContextPath());
629         return urlBuilder;
630     }
631 
632     /**
633      * Builds a URL to a path that is meant to be relative to the Servlet context.
634      * 
635      * @param httpRequest current HTTP request
636      * @param path path relative to the context, may start with a "/"
637      * 
638      * @return URL builder containing the scheme, server name, server port, and full path
639      */
640     public static URLBuilder getContextRelativeUrl(HttpServletRequest httpRequest, String path) {
641         URLBuilder urlBuilder = new URLBuilder();
642         urlBuilder.setScheme(httpRequest.getScheme());
643         urlBuilder.setHost(httpRequest.getServerName());
644         urlBuilder.setPort(httpRequest.getServerPort());
645 
646         StringBuilder pathBuilder = new StringBuilder();
647         if (!"".equals(httpRequest.getContextPath())) {
648             pathBuilder.append(httpRequest.getContextPath());
649         }
650         if (!path.startsWith("/")) {
651             pathBuilder.append("/");
652         }
653         pathBuilder.append(DatatypeHelper.safeTrim(path));
654         urlBuilder.setPath(pathBuilder.toString());
655 
656         return urlBuilder;
657     }
658 }