001/* 002 * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl-2.1.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Nelson Silva <nelson.silva@inevo.pt> 016 */ 017package org.nuxeo.ecm.platform.auth.saml; 018 019import org.apache.commons.codec.binary.Base64; 020import org.apache.commons.lang.StringUtils; 021import org.apache.commons.logging.Log; 022import org.apache.commons.logging.LogFactory; 023import org.nuxeo.common.utils.i18n.I18NUtils; 024import org.nuxeo.ecm.platform.api.login.UserIdentificationInfo; 025import org.nuxeo.ecm.platform.auth.saml.binding.HTTPPostBinding; 026import org.nuxeo.ecm.platform.auth.saml.binding.HTTPRedirectBinding; 027import org.nuxeo.ecm.platform.auth.saml.binding.SAMLBinding; 028import org.nuxeo.ecm.platform.auth.saml.key.KeyManager; 029import org.nuxeo.ecm.platform.auth.saml.slo.SLOProfile; 030import org.nuxeo.ecm.platform.auth.saml.slo.SLOProfileImpl; 031import org.nuxeo.ecm.platform.auth.saml.sso.WebSSOProfile; 032import org.nuxeo.ecm.platform.auth.saml.sso.WebSSOProfileImpl; 033import org.nuxeo.ecm.platform.auth.saml.user.EmailBasedUserResolver; 034import org.nuxeo.ecm.platform.auth.saml.user.UserMapperBasedResolver; 035import org.nuxeo.ecm.platform.auth.saml.user.AbstractUserResolver; 036import org.nuxeo.ecm.platform.auth.saml.user.UserResolver; 037import org.nuxeo.ecm.platform.ui.web.auth.LoginScreenHelper; 038import org.nuxeo.ecm.platform.ui.web.auth.NXAuthConstants; 039import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPlugin; 040import org.nuxeo.ecm.platform.ui.web.auth.interfaces.NuxeoAuthenticationPluginLogoutExtension; 041import org.nuxeo.ecm.platform.ui.web.auth.service.LoginProviderLinkComputer; 042import org.nuxeo.runtime.api.Framework; 043import org.nuxeo.usermapper.service.UserMapperService; 044import org.opensaml.DefaultBootstrap; 045import org.opensaml.common.SAMLException; 046import org.opensaml.common.SAMLObject; 047import org.opensaml.common.binding.BasicSAMLMessageContext; 048import org.opensaml.common.binding.SAMLMessageContext; 049import org.opensaml.common.xml.SAMLConstants; 050import org.opensaml.saml2.core.AuthnRequest; 051import org.opensaml.saml2.core.LogoutRequest; 052import org.opensaml.saml2.core.LogoutResponse; 053import org.opensaml.saml2.core.NameID; 054import org.opensaml.saml2.encryption.Decrypter; 055import org.opensaml.saml2.encryption.EncryptedElementTypeEncryptedKeyResolver; 056import org.opensaml.saml2.metadata.EntityDescriptor; 057import org.opensaml.saml2.metadata.IDPSSODescriptor; 058import org.opensaml.saml2.metadata.RoleDescriptor; 059import org.opensaml.saml2.metadata.SPSSODescriptor; 060import org.opensaml.saml2.metadata.SingleLogoutService; 061import org.opensaml.saml2.metadata.SingleSignOnService; 062import org.opensaml.saml2.metadata.provider.AbstractMetadataProvider; 063import org.opensaml.saml2.metadata.provider.FilesystemMetadataProvider; 064import org.opensaml.saml2.metadata.provider.HTTPMetadataProvider; 065import org.opensaml.saml2.metadata.provider.MetadataProvider; 066import org.opensaml.saml2.metadata.provider.MetadataProviderException; 067import org.opensaml.security.MetadataCredentialResolver; 068import org.opensaml.util.URLBuilder; 069import org.opensaml.ws.message.decoder.MessageDecodingException; 070import org.opensaml.ws.transport.InTransport; 071import org.opensaml.ws.transport.http.HttpServletRequestAdapter; 072import org.opensaml.ws.transport.http.HttpServletResponseAdapter; 073import org.opensaml.xml.Configuration; 074import org.opensaml.xml.ConfigurationException; 075import org.opensaml.xml.encryption.ChainingEncryptedKeyResolver; 076import org.opensaml.xml.encryption.InlineEncryptedKeyResolver; 077import org.opensaml.xml.encryption.SimpleRetrievalMethodEncryptedKeyResolver; 078import org.opensaml.xml.io.Marshaller; 079import org.opensaml.xml.io.MarshallingException; 080import org.opensaml.xml.parse.BasicParserPool; 081import org.opensaml.xml.security.credential.Credential; 082import org.opensaml.xml.security.keyinfo.KeyInfoCredentialResolver; 083import org.opensaml.xml.security.keyinfo.StaticKeyInfoCredentialResolver; 084import org.opensaml.xml.signature.SignatureTrustEngine; 085import org.opensaml.xml.signature.impl.ExplicitKeySignatureTrustEngine; 086import org.opensaml.xml.util.Pair; 087import org.opensaml.xml.util.XMLHelper; 088import org.w3c.dom.Element; 089 090import javax.servlet.ServletException; 091import javax.servlet.http.Cookie; 092import javax.servlet.http.HttpServletRequest; 093import javax.servlet.http.HttpServletResponse; 094import javax.servlet.http.HttpSession; 095 096import java.io.File; 097import java.io.IOException; 098import java.io.StringWriter; 099import java.util.ArrayList; 100import java.util.HashMap; 101import java.util.List; 102import java.util.Map; 103 104import static org.nuxeo.ecm.platform.ui.web.auth.NXAuthConstants.LOGIN_ERROR; 105 106/** 107 * A SAML2 authentication provider. 108 * 109 * @since 6.0 110 */ 111public class SAMLAuthenticationProvider implements NuxeoAuthenticationPlugin, LoginProviderLinkComputer, 112 NuxeoAuthenticationPluginLogoutExtension { 113 114 private static final Log log = LogFactory.getLog(SAMLAuthenticationProvider.class); 115 116 private static final String ERROR_PAGE = "saml/error.jsp"; 117 private static final String ERROR_AUTH = "error.saml.auth"; 118 private static final String ERROR_USER = "error.saml.userMapping"; 119 120 // User Resolver 121 private static final Class<? extends UserResolver> DEFAULT_USER_RESOLVER_CLASS = EmailBasedUserResolver.class; 122 private static final Class<? extends UserResolver> USERMAPPER_USER_RESOLVER_CLASS = UserMapperBasedResolver.class; 123 124 125 // SAML Constants 126 static final String SAML_SESSION_KEY = "SAML_SESSION"; 127 128 // Supported SAML Bindings 129 // TODO: Allow registering new bindings 130 static List<SAMLBinding> bindings = new ArrayList<>(); 131 static { 132 bindings.add(new HTTPPostBinding()); 133 bindings.add(new HTTPRedirectBinding()); 134 } 135 136 // Decryption key resolver 137 private static ChainingEncryptedKeyResolver encryptedKeyResolver = new ChainingEncryptedKeyResolver(); 138 static { 139 encryptedKeyResolver.getResolverChain().add(new InlineEncryptedKeyResolver()); 140 encryptedKeyResolver.getResolverChain().add(new EncryptedElementTypeEncryptedKeyResolver()); 141 encryptedKeyResolver.getResolverChain().add(new SimpleRetrievalMethodEncryptedKeyResolver()); 142 } 143 144 // Profiles supported by the IdP 145 private Map<String, AbstractSAMLProfile> profiles = new HashMap<>(); 146 147 private UserResolver userResolver; 148 149 private KeyManager keyManager; 150 151 private SignatureTrustEngine trustEngine; 152 153 private Decrypter decrypter; 154 155 private MetadataProvider metadataProvider; 156 157 @Override 158 public void initPlugin(Map<String, String> parameters) { 159 160 // Initialize the User Resolver 161 String userResolverClassname = parameters.get("userResolverClass"); 162 Class<? extends UserResolver> userResolverClass = null; 163 if (StringUtils.isBlank(userResolverClassname)) { 164 UserMapperService ums = Framework.getService(UserMapperService.class); 165 if (ums!=null) { 166 userResolverClass = USERMAPPER_USER_RESOLVER_CLASS; 167 } else { 168 userResolverClass = DEFAULT_USER_RESOLVER_CLASS; 169 } 170 } else { 171 try { 172 userResolverClass = Class.forName(userResolverClassname).asSubclass(AbstractUserResolver.class); 173 } catch (ClassNotFoundException e) { 174 log.error("Failed get user resolver class " + userResolverClassname); 175 } 176 177 } 178 try { 179 userResolver = userResolverClass.newInstance(); 180 userResolver.init(parameters); 181 } catch (InstantiationException | IllegalAccessException e) { 182 log.error("Failed to initialize user resolver " + userResolverClassname); 183 } 184 185 // Initialize the OpenSAML library 186 try { 187 DefaultBootstrap.bootstrap(); 188 } catch (ConfigurationException e) { 189 log.error("Failed to bootstrap OpenSAML", e); 190 } 191 192 // Read the IdP metadata and initialize the supported profiles 193 try { 194 // Read the IdP metadata 195 initializeMetadataProvider(parameters); 196 197 // Setup Signature Trust Engine 198 MetadataCredentialResolver metadataCredentialResolver = new MetadataCredentialResolver(metadataProvider); 199 trustEngine = new ExplicitKeySignatureTrustEngine( 200 metadataCredentialResolver, 201 org.opensaml.xml.Configuration.getGlobalSecurityConfiguration().getDefaultKeyInfoCredentialResolver()); 202 203 // Setup decrypter 204 Credential encryptionCredential = getKeyManager().getEncryptionCredential(); 205 if (encryptionCredential != null) { 206 KeyInfoCredentialResolver resolver = new StaticKeyInfoCredentialResolver(encryptionCredential); 207 decrypter = new Decrypter(null, resolver, encryptedKeyResolver); 208 decrypter.setRootInNewDocument(true); 209 } 210 211 // Process IdP roles 212 for (RoleDescriptor roleDescriptor : getIdPDescriptor().getRoleDescriptors()) { 213 214 // Web SSO role 215 if (roleDescriptor.getElementQName().equals(IDPSSODescriptor.DEFAULT_ELEMENT_NAME) 216 && roleDescriptor.isSupportedProtocol(org.opensaml.common.xml.SAMLConstants.SAML20P_NS)) { 217 218 IDPSSODescriptor idpSSO = (IDPSSODescriptor) roleDescriptor; 219 220 // SSO 221 for (SingleSignOnService sso : idpSSO.getSingleSignOnServices()) { 222 if (sso.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI)) { 223 addProfile(new WebSSOProfileImpl(sso)); 224 break; 225 } 226 } 227 228 // SLO 229 for (SingleLogoutService slo : idpSSO.getSingleLogoutServices()) { 230 if (slo.getBinding().equals(SAMLConstants.SAML2_REDIRECT_BINDING_URI)) { 231 addProfile(new SLOProfileImpl(slo)); 232 break; 233 } 234 } 235 } 236 } 237 238 } catch (MetadataProviderException e) { 239 log.warn("Failed to register IdP: " + e.getMessage()); 240 } 241 242 // contribute icon and link to the Login Screen 243 if (StringUtils.isNotBlank(parameters.get("name"))) { 244 LoginScreenHelper.registerLoginProvider(parameters.get("name"), parameters.get("icon"), null, 245 parameters.get("label"), parameters.get("description"), this); 246 } 247 } 248 249 private void addProfile(AbstractSAMLProfile profile) { 250 profile.setTrustEngine(trustEngine); 251 profile.setDecrypter(decrypter); 252 profiles.put(profile.getProfileIdentifier(), profile); 253 } 254 255 private void initializeMetadataProvider(Map<String, String> parameters) throws MetadataProviderException { 256 AbstractMetadataProvider metadataProvider; 257 258 String metadataUrl = parameters.get("metadata"); 259 if (metadataUrl == null) { 260 throw new MetadataProviderException("No metadata URI set for provider " 261 + ((parameters.containsKey("name")) ? parameters.get("name") : "")); 262 } 263 264 int requestTimeout = parameters.containsKey("timeout") ? Integer.parseInt(parameters.get("timeout")) : 5; 265 266 if (metadataUrl.startsWith("http:") || metadataUrl.startsWith("https:")) { 267 metadataProvider = new HTTPMetadataProvider(metadataUrl, requestTimeout * 1000); 268 } else { // file 269 metadataProvider = new FilesystemMetadataProvider(new File(metadataUrl)); 270 } 271 272 metadataProvider.setParserPool(new BasicParserPool()); 273 metadataProvider.initialize(); 274 275 this.metadataProvider = metadataProvider; 276 } 277 278 private EntityDescriptor getIdPDescriptor() throws MetadataProviderException { 279 return (EntityDescriptor) metadataProvider.getMetadata(); 280 } 281 282 /** 283 * Returns a Login URL to use with HTTP Redirect 284 */ 285 protected String getSSOUrl(HttpServletRequest request, HttpServletResponse response) { 286 WebSSOProfile sso = (WebSSOProfile) profiles.get(WebSSOProfile.PROFILE_URI); 287 if (sso == null) { 288 return null; 289 } 290 291 // Create and populate the context 292 SAMLMessageContext context = new BasicSAMLMessageContext(); 293 populateLocalContext(context); 294 295 // Store the requested URL in the Relay State 296 String requestedUrl = getRequestedUrl(request); 297 if (requestedUrl != null) { 298 context.setRelayState(requestedUrl); 299 } 300 301 // Build Uri 302 HTTPRedirectBinding binding = (HTTPRedirectBinding) getBinding(SAMLConstants.SAML2_REDIRECT_BINDING_URI); 303 String loginURL = sso.getEndpoint().getLocation(); 304 try { 305 AuthnRequest authnRequest = sso.buildAuthRequest(request); 306 authnRequest.setDestination(sso.getEndpoint().getLocation()); 307 context.setOutboundSAMLMessage(authnRequest); 308 loginURL = binding.buildRedirectURL(context, sso.getEndpoint().getLocation()); 309 } catch (SAMLException e) { 310 log.error("Failed to build redirect URL", e); 311 } 312 return loginURL; 313 } 314 315 private String getRequestedUrl(HttpServletRequest request) { 316 String requestedUrl = (String) request.getAttribute(NXAuthConstants.REQUESTED_URL); 317 if (requestedUrl == null) { 318 HttpSession session = request.getSession(false); 319 if (session != null) { 320 requestedUrl = (String) session.getAttribute(NXAuthConstants.START_PAGE_SAVE_KEY); 321 } 322 } 323 return requestedUrl; 324 } 325 326 @Override 327 public String computeUrl(HttpServletRequest request, String requestedUrl) { 328 return getSSOUrl(request, null); 329 } 330 331 @Override 332 public Boolean handleLoginPrompt(HttpServletRequest request, HttpServletResponse response, String baseURL) { 333 334 String loginError = (String) request.getAttribute(LOGIN_ERROR); 335 if (loginError != null) { 336 try { 337 request.getRequestDispatcher(ERROR_PAGE).forward(request, response); 338 return true; 339 } catch (ServletException | IOException e) { 340 log.error("Failed to redirect to error page", e); 341 return false; 342 } 343 } 344 345 String loginURL = getSSOUrl(request, response); 346 try { 347 response.sendRedirect(loginURL); 348 } catch (IOException e) { 349 String errorMessage = String.format("Unable to send redirect on %s", loginURL); 350 log.error(errorMessage, e); 351 return false; 352 } 353 return true; 354 } 355 356 // Retrieves user identification information from the request. 357 @Override 358 public UserIdentificationInfo handleRetrieveIdentity(HttpServletRequest request, HttpServletResponse response) { 359 360 HttpServletRequestAdapter inTransport = new HttpServletRequestAdapter(request); 361 SAMLBinding binding = getBinding(inTransport); 362 363 // Check if we support this binding 364 if (binding == null) { 365 return null; 366 } 367 368 HttpServletResponseAdapter outTransport = new HttpServletResponseAdapter(response, request.isSecure()); 369 370 // Create and populate the context 371 SAMLMessageContext context = new BasicSAMLMessageContext(); 372 context.setInboundMessageTransport(inTransport); 373 context.setOutboundMessageTransport(outTransport); 374 populateLocalContext(context); 375 376 // Decode the message 377 try { 378 binding.decode(context); 379 } catch (org.opensaml.xml.security.SecurityException | MessageDecodingException e) { 380 log.error("Error during SAML decoding", e); 381 return null; 382 } 383 384 // Set Peer context info if needed 385 try { 386 if (context.getPeerEntityId() == null) { 387 context.setPeerEntityId(getIdPDescriptor().getEntityID()); 388 } 389 if (context.getPeerEntityMetadata() == null) { 390 context.setPeerEntityMetadata(getIdPDescriptor()); 391 } 392 if (context.getPeerEntityRole() == null) { 393 context.setPeerEntityRole(IDPSSODescriptor.DEFAULT_ELEMENT_NAME); 394 } 395 } catch (MetadataProviderException e) { 396 // 397 } 398 399 // Check for a response processor for this profile 400 AbstractSAMLProfile processor = getProcessor(context); 401 402 if (processor == null) { 403 log.warn("Unsupported profile encountered in the context " + context.getCommunicationProfileId()); 404 return null; 405 } 406 407 // Set the communication profile 408 context.setCommunicationProfileId(processor.getProfileIdentifier()); 409 410 // Delegate handling the message to the processor 411 SAMLObject message = context.getInboundSAMLMessage(); 412 413 // Handle SLO 414 // TODO - Try to handle IdP initiated SLO somewhere else 415 if (processor instanceof SLOProfile) { 416 SLOProfile slo = (SLOProfile) processor; 417 try { 418 // Handle SLO response 419 if (message instanceof LogoutResponse) { 420 slo.processLogoutResponse(context); 421 // Handle SLO request 422 } else if (message instanceof LogoutRequest) { 423 SAMLCredential credential = getSamlCredential(request); 424 slo.processLogoutRequest(context, credential); 425 } 426 } catch (SAMLException e) { 427 log.debug("Error processing SAML message", e); 428 } 429 return null; 430 } 431 432 // Handle SSO 433 SAMLCredential credential; 434 435 try { 436 credential = ((WebSSOProfile) processor).processAuthenticationResponse(context); 437 } catch (SAMLException e) { 438 log.error("Error processing SAML message", e); 439 sendError(request, ERROR_AUTH); 440 return null; 441 } 442 443 String userId = userResolver.findOrCreateNuxeoUser(credential); 444 445 if (userId == null) { 446 log.warn("Failed to resolve user with NameID \"" + credential.getNameID().getValue() + "\"."); 447 sendError(request, ERROR_USER); 448 return null; 449 } 450 451 // Store session id in a cookie 452 if (credential.getSessionIndexes() != null && !credential.getSessionIndexes().isEmpty()) { 453 String nameValue = credential.getNameID().getValue(); 454 String nameFormat = credential.getNameID().getFormat(); 455 String sessionId = credential.getSessionIndexes().get(0); 456 addCookie(response, SAML_SESSION_KEY, sessionId + "|" + nameValue + "|" + nameFormat); 457 } 458 459 return new UserIdentificationInfo(userId, userId); 460 } 461 462 protected AbstractSAMLProfile getProcessor(SAMLMessageContext context) { 463 String profileId; 464 SAMLObject message = context.getInboundSAMLMessage(); 465 if (message instanceof LogoutResponse || message instanceof LogoutRequest) { 466 profileId = SLOProfile.PROFILE_URI; 467 } else { 468 profileId = WebSSOProfile.PROFILE_URI; 469 } 470 471 return profiles.get(profileId); 472 } 473 474 protected SAMLBinding getBinding(String bindingURI) { 475 for (SAMLBinding binding : bindings) { 476 if (binding.getBindingURI().equals(bindingURI)) { 477 return binding; 478 } 479 } 480 return null; 481 } 482 483 protected SAMLBinding getBinding(InTransport transport) { 484 for (SAMLBinding binding : bindings) { 485 if (binding.supports(transport)) { 486 return binding; 487 } 488 } 489 return null; 490 } 491 492 private void populateLocalContext(SAMLMessageContext context) { 493 // Set local info 494 context.setLocalEntityId(SAMLConfiguration.getEntityId()); 495 context.setLocalEntityRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME); 496 context.setMetadataProvider(metadataProvider); 497 498 // Set the signing key 499 keyManager = Framework.getLocalService(KeyManager.class); 500 if (getKeyManager().getSigningCredential() != null) { 501 context.setOutboundSAMLMessageSigningCredential(getKeyManager().getSigningCredential()); 502 } 503 } 504 505 @Override 506 public Boolean needLoginPrompt(HttpServletRequest httpRequest) { 507 return true; 508 } 509 510 @Override 511 public List<String> getUnAuthenticatedURLPrefix() { 512 return null; 513 } 514 515 /** 516 * Returns a Logout URL to use with HTTP Redirect 517 */ 518 protected String getSLOUrl(HttpServletRequest request, HttpServletResponse response) { 519 SLOProfile slo = (SLOProfile) profiles.get(SLOProfile.PROFILE_URI); 520 if (slo == null) { 521 return null; 522 } 523 524 String logoutURL = slo.getEndpoint().getLocation(); 525 526 SAMLCredential credential = getSamlCredential(request); 527 528 // Create and populate the context 529 SAMLMessageContext context = new BasicSAMLMessageContext(); 530 populateLocalContext(context); 531 532 try { 533 LogoutRequest logoutRequest = slo.buildLogoutRequest(context, credential); 534 Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(logoutRequest); 535 if (marshaller == null) { 536 log.error("Unable to marshall message, no marshaller registered " + "for message object: " 537 + logoutRequest.getElementQName()); 538 } 539 Element dom = marshaller.marshall(logoutRequest); 540 StringWriter buffer = new StringWriter(); 541 XMLHelper.writeNode(dom, buffer); 542 String encodedSaml = Base64.encodeBase64String(buffer.toString().getBytes()); 543 544 // Add the SAML as parameter 545 URLBuilder urlBuilder = new URLBuilder(logoutURL); 546 urlBuilder.getQueryParams().add(new Pair<>(HTTPRedirectBinding.SAML_REQUEST, encodedSaml)); 547 logoutURL = urlBuilder.buildURL(); 548 } catch (SAMLException e) { 549 log.error("Failed to get SAML Logout request", e); 550 } catch (MarshallingException e) { 551 log.error("Encountered error marshalling message to its DOM representation", e); 552 } 553 554 return logoutURL; 555 } 556 557 private SAMLCredential getSamlCredential(HttpServletRequest request) { 558 SAMLCredential credential = null; 559 560 // Retrieve the SAMLCredential credential from cookie 561 Cookie cookie = getCookie(request, SAML_SESSION_KEY); 562 if (cookie != null) { 563 String[] parts = cookie.getValue().split("\\|"); 564 String sessionId = parts[0]; 565 String nameValue = parts[1]; 566 String nameFormat = parts[2]; 567 568 NameID nameID = (NameID) Configuration.getBuilderFactory().getBuilder(NameID.DEFAULT_ELEMENT_NAME).buildObject( 569 NameID.DEFAULT_ELEMENT_NAME); 570 nameID.setValue(nameValue); 571 nameID.setFormat(nameFormat); 572 573 List<String> sessionIndexes = new ArrayList<>(); 574 sessionIndexes.add(sessionId); 575 576 credential = new SAMLCredential(nameID, sessionIndexes); 577 } 578 579 return credential; 580 } 581 582 @Override 583 public Boolean handleLogout(HttpServletRequest request, HttpServletResponse response) { 584 String logoutURL = getSLOUrl(request, response); 585 586 if (logoutURL == null) { 587 return false; 588 } 589 590 if (log.isDebugEnabled()) { 591 log.debug("Send redirect to " + logoutURL); 592 } 593 594 try { 595 response.sendRedirect(logoutURL); 596 } catch (IOException e) { 597 String errorMessage = String.format("Unable to send redirect on %s", logoutURL); 598 log.error(errorMessage, e); 599 return false; 600 } 601 602 Cookie cookie = getCookie(request, SAML_SESSION_KEY); 603 if (cookie != null) { 604 removeCookie(response, cookie); 605 } 606 607 return true; 608 } 609 610 private void sendError(HttpServletRequest req, String key) { 611 String msg = I18NUtils.getMessageString("messages", key, null, req.getLocale()); 612 req.setAttribute(LOGIN_ERROR, msg); 613 } 614 615 private KeyManager getKeyManager() { 616 if (keyManager == null) { 617 keyManager = Framework.getLocalService(KeyManager.class); 618 } 619 return keyManager; 620 } 621 622 private void addCookie(HttpServletResponse httpResponse, String name, String value) { 623 Cookie cookie = new Cookie(name, value); 624 httpResponse.addCookie(cookie); 625 } 626 627 private Cookie getCookie(HttpServletRequest httpRequest, String cookieName) { 628 Cookie cookies[] = httpRequest.getCookies(); 629 if (cookies != null) { 630 for (Cookie cooky : cookies) { 631 if (cookieName.equals(cooky.getName())) { 632 return cooky; 633 } 634 } 635 } 636 return null; 637 } 638 639 private void removeCookie(HttpServletResponse httpResponse, Cookie cookie) { 640 log.debug(String.format("Removing cookie %s.", cookie.getName())); 641 cookie.setMaxAge(0); 642 cookie.setValue(""); 643 httpResponse.addCookie(cookie); 644 } 645}