View Javadoc

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