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