1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package edu.internet2.middleware.shibboleth.idp.authn;
18
19 import java.io.IOException;
20 import java.security.GeneralSecurityException;
21 import java.security.MessageDigest;
22 import java.security.NoSuchAlgorithmException;
23 import java.security.Principal;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.Map.Entry;
32
33 import javax.security.auth.Subject;
34 import javax.servlet.RequestDispatcher;
35 import javax.servlet.ServletConfig;
36 import javax.servlet.ServletException;
37 import javax.servlet.http.Cookie;
38 import javax.servlet.http.HttpServlet;
39 import javax.servlet.http.HttpServletRequest;
40 import javax.servlet.http.HttpServletResponse;
41
42 import org.joda.time.DateTime;
43 import org.opensaml.common.IdentifierGenerator;
44 import org.opensaml.common.impl.SecureRandomIdentifierGenerator;
45 import org.opensaml.saml2.core.AuthnContext;
46 import org.opensaml.util.storage.StorageService;
47 import org.opensaml.ws.transport.http.HTTPTransportUtils;
48 import org.opensaml.xml.util.Base64;
49 import org.opensaml.xml.util.DatatypeHelper;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 import edu.internet2.middleware.shibboleth.common.session.SessionManager;
54 import edu.internet2.middleware.shibboleth.common.util.HttpHelper;
55 import edu.internet2.middleware.shibboleth.idp.profile.IdPProfileHandlerManager;
56 import edu.internet2.middleware.shibboleth.idp.session.AuthenticationMethodInformation;
57 import edu.internet2.middleware.shibboleth.idp.session.ServiceInformation;
58 import edu.internet2.middleware.shibboleth.idp.session.Session;
59 import edu.internet2.middleware.shibboleth.idp.session.impl.AuthenticationMethodInformationImpl;
60 import edu.internet2.middleware.shibboleth.idp.session.impl.ServiceInformationImpl;
61
62
63 public class AuthenticationEngine extends HttpServlet {
64
65
66 public static final String LOGIN_CONTEXT_PARTITION_NAME_INIT_PARAM_NAME = "loginContextPartitionName";
67
68
69 public static final String LOGIN_CONTEXT_LIFETIME_INIT_PARAM_NAME = "loginContextEntryLifetime";
70
71
72 public static final String IDP_SESSION_COOKIE_NAME = "_idp_session";
73
74
75 public static final String LOGIN_CONTEXT_KEY_NAME = "_idp_authn_lc_key";
76
77
78 private static final long serialVersionUID = -8479060989001890156L;
79
80
81 private static final Logger LOG = LoggerFactory.getLogger(AuthenticationEngine.class);
82
83
84 private static StorageService<String, LoginContextEntry> storageService;
85
86
87 private static String loginContextPartitionName;
88
89
90 private static long loginContextEntryLifetime;
91
92
93 private static IdentifierGenerator idGen;
94
95
96 private IdPProfileHandlerManager handlerManager;
97
98
99 private SessionManager<Session> sessionManager;
100
101
102 public void init(ServletConfig config) throws ServletException {
103 super.init(config);
104
105 String handlerManagerId = config.getInitParameter("handlerManagerId");
106 if (DatatypeHelper.isEmpty(handlerManagerId)) {
107 handlerManagerId = "shibboleth.HandlerManager";
108 }
109 handlerManager = (IdPProfileHandlerManager) getServletContext().getAttribute(handlerManagerId);
110
111 String sessionManagerId = config.getInitParameter("sessionManagedId");
112 if (DatatypeHelper.isEmpty(sessionManagerId)) {
113 sessionManagerId = "shibboleth.SessionManager";
114 }
115 sessionManager = (SessionManager<Session>) getServletContext().getAttribute(sessionManagerId);
116
117 String storageServiceId = config.getInitParameter("storageServiceId");
118 if (DatatypeHelper.isEmpty(storageServiceId)) {
119 storageServiceId = "shibboleth.StorageService";
120 }
121 storageService = (StorageService<String, LoginContextEntry>) getServletContext().getAttribute(storageServiceId);
122
123 String partitionName = DatatypeHelper.safeTrimOrNullString(config
124 .getInitParameter(LOGIN_CONTEXT_PARTITION_NAME_INIT_PARAM_NAME));
125 if (partitionName != null) {
126 loginContextPartitionName = partitionName;
127 } else {
128 loginContextPartitionName = "loginContexts";
129 }
130
131 String lifetime = DatatypeHelper.safeTrimOrNullString(config
132 .getInitParameter(LOGIN_CONTEXT_LIFETIME_INIT_PARAM_NAME));
133 if (lifetime != null) {
134 loginContextEntryLifetime = Long.parseLong(lifetime);
135 } else {
136 loginContextEntryLifetime = 1000 * 60 * 30;
137 }
138
139 try {
140 idGen = new SecureRandomIdentifierGenerator();
141 } catch (NoSuchAlgorithmException e) {
142 throw new ServletException("Error create random number generator", e);
143 }
144 }
145
146
147
148
149
150
151
152
153
154
155 protected static LoginContext retrieveLoginContext(HttpServletRequest httpRequest, boolean removeFromStorageService) {
156
157
158
159
160 LoginContext loginContext = (LoginContext) httpRequest.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
161 if (loginContext != null) {
162 LOG.trace("Login context retrieved from HTTP request attribute");
163 return loginContext;
164 }
165
166 String contextId = DatatypeHelper.safeTrimOrNullString((String) httpRequest
167 .getAttribute(LOGIN_CONTEXT_KEY_NAME));
168
169 if (contextId == null) {
170 Cookie[] requestCookies = httpRequest.getCookies();
171 if (requestCookies != null) {
172 for (Cookie requestCookie : requestCookies) {
173 if (DatatypeHelper.safeEquals(requestCookie.getName(), LOGIN_CONTEXT_KEY_NAME)) {
174 LOG.trace("Located cookie with login context key");
175 contextId = requestCookie.getValue();
176 break;
177 }
178 }
179 }
180 }
181
182 LOG.trace("Using login context key {} to look up login context", contextId);
183 LoginContextEntry entry;
184 if (removeFromStorageService) {
185 entry = storageService.remove(loginContextPartitionName, contextId);
186 } else {
187 entry = storageService.get(loginContextPartitionName, contextId);
188 }
189 if (entry == null) {
190 LOG.trace("No entry for login context found in storage service.");
191 return null;
192 } else if (entry.isExpired()) {
193 LOG.trace("Login context entry found in storage service but it was expired.");
194 return null;
195 } else {
196 LOG.trace("Login context entry found in storage service.");
197 return entry.getLoginContext();
198 }
199 }
200
201
202
203
204
205
206
207 public static void returnToAuthenticationEngine(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
208 LOG.debug("Returning control to authentication engine");
209 LoginContext loginContext = retrieveLoginContext(httpRequest, false);
210 if (loginContext == null) {
211 LOG.error("No login context available, unable to return to authentication engine");
212 forwardRequest("/idp-error.jsp", httpRequest, httpResponse);
213 } else {
214 forwardRequest(loginContext.getAuthenticationEngineURL(), httpRequest, httpResponse);
215 }
216 }
217
218
219
220
221
222
223
224
225 public static void returnToProfileHandler(LoginContext loginContext, HttpServletRequest httpRequest,
226 HttpServletResponse httpResponse) {
227 LOG.debug("Returning control to profile handler at: {}", loginContext.getProfileHandlerURL());
228 httpRequest.setAttribute(LoginContext.LOGIN_CONTEXT_KEY, loginContext);
229
230
231 Cookie lcKeyCookie = new Cookie(LOGIN_CONTEXT_KEY_NAME, "");
232 lcKeyCookie.setMaxAge(0);
233 httpResponse.addCookie(lcKeyCookie);
234
235 forwardRequest(loginContext.getProfileHandlerURL(), httpRequest, httpResponse);
236 }
237
238
239
240
241
242
243
244
245 protected static void forwardRequest(String forwardPath, HttpServletRequest httpRequest,
246 HttpServletResponse httpResponse) {
247 try {
248 RequestDispatcher dispatcher = httpRequest.getRequestDispatcher(forwardPath);
249 dispatcher.forward(httpRequest, httpResponse);
250 return;
251 } catch (IOException e) {
252 LOG.error("Unable to return control back to authentication engine", e);
253 } catch (ServletException e) {
254 LOG.error("Unable to return control back to authentication engine", e);
255 }
256 }
257
258
259 @SuppressWarnings("unchecked")
260 protected void service(HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws ServletException,
261 IOException {
262 LOG.debug("Processing incoming request");
263
264 if (httpResponse.isCommitted()) {
265 LOG.error("HTTP Response already committed");
266 }
267
268 LoginContext loginContext = retrieveLoginContext(httpRequest, true);
269 if (loginContext == null) {
270 LOG.error("Incoming request does not have attached login context");
271 throw new ServletException("Incoming request does not have attached login context");
272 }
273
274 if (!loginContext.getAuthenticationAttempted()) {
275 startUserAuthentication(loginContext, httpRequest, httpResponse);
276 } else {
277 completeAuthentication(loginContext, httpRequest, httpResponse);
278 }
279 }
280
281
282
283
284
285
286
287
288
289
290 protected void startUserAuthentication(LoginContext loginContext, HttpServletRequest httpRequest,
291 HttpServletResponse httpResponse) {
292 LOG.debug("Beginning user authentication process.");
293 try {
294 Session idpSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
295 if (idpSession != null) {
296 LOG.debug("Existing IdP session available for principal {}", idpSession.getPrincipalName());
297 }
298
299 Map<String, LoginHandler> possibleLoginHandlers = determinePossibleLoginHandlers(idpSession, loginContext);
300 LOG.debug("Possible authentication handlers for this request: {}", possibleLoginHandlers);
301
302
303 if (loginContext.isForceAuthRequired()) {
304 filterByForceAuthentication(idpSession, loginContext, possibleLoginHandlers);
305 }
306
307 if (loginContext.isPassiveAuthRequired()) {
308 filterByPassiveAuthentication(idpSession, loginContext, possibleLoginHandlers);
309 }
310
311
312
313 LOG.debug("Possible authentication handlers after filtering: {}", possibleLoginHandlers);
314 LoginHandler loginHandler;
315 if (idpSession != null && possibleLoginHandlers.containsKey(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX)) {
316 loginContext.setAttemptedAuthnMethod(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
317 loginHandler = possibleLoginHandlers.get(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
318 } else {
319 possibleLoginHandlers.remove(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
320 Entry<String, LoginHandler> chosenLoginHandler = possibleLoginHandlers.entrySet().iterator().next();
321 loginContext.setAttemptedAuthnMethod(chosenLoginHandler.getKey());
322 loginHandler = chosenLoginHandler.getValue();
323 }
324
325
326 LOG.debug("Authenticating user with login handler of type {}", loginHandler.getClass().getName());
327 loginContext.setAuthenticationAttempted();
328 loginContext.setAuthenticationEngineURL(HttpHelper.getRequestUriWithoutContext(httpRequest));
329 storeLoginContext(loginContext, httpRequest, httpResponse);
330 loginHandler.login(httpRequest, httpResponse);
331 } catch (AuthenticationException e) {
332 loginContext.setAuthenticationFailure(e);
333 returnToProfileHandler(loginContext, httpRequest, httpResponse);
334 }
335 }
336
337
338
339
340
341
342
343
344
345
346
347 protected Map<String, LoginHandler> determinePossibleLoginHandlers(Session idpSession, LoginContext loginContext)
348 throws AuthenticationException {
349 Map<String, LoginHandler> supportedLoginHandlers = new HashMap<String, LoginHandler>(handlerManager
350 .getLoginHandlers());
351 LOG.debug("Filtering configured login handlers by requested athentication methods.");
352 LOG.debug("Configured LoginHandlers: {}", supportedLoginHandlers);
353 LOG.debug("Requested authentication methods: {}", loginContext.getRequestedAuthenticationMethods());
354
355
356 if (loginContext.getRequestedAuthenticationMethods().isEmpty()) {
357 LOG.trace("No preference given for authentication methods");
358 return supportedLoginHandlers;
359 }
360
361
362
363
364 if (supportedLoginHandlers.containsKey(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX) && idpSession != null
365 && loginContext.getRequestedAuthenticationMethods() != null) {
366 boolean retainPreviousSession = false;
367
368 Map<String, AuthenticationMethodInformation> currentAuthnMethods = idpSession.getAuthenticationMethods();
369 for (String currentAuthnMethod : currentAuthnMethods.keySet()) {
370 if (loginContext.getRequestedAuthenticationMethods().contains(currentAuthnMethod)) {
371 retainPreviousSession = true;
372 break;
373 }
374 }
375
376 if (!retainPreviousSession) {
377 supportedLoginHandlers.remove(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
378 }
379 }
380
381
382
383 Iterator<Entry<String, LoginHandler>> supportedLoginHandlerItr = supportedLoginHandlers.entrySet().iterator();
384 Entry<String, LoginHandler> supportedLoginHandler;
385 while (supportedLoginHandlerItr.hasNext()) {
386 supportedLoginHandler = supportedLoginHandlerItr.next();
387 if (!supportedLoginHandler.getKey().equals(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX)
388 && !loginContext.getRequestedAuthenticationMethods().contains(supportedLoginHandler.getKey())) {
389 supportedLoginHandlerItr.remove();
390 continue;
391 }
392 }
393
394 if (supportedLoginHandlers.isEmpty()) {
395 LOG.error("No authentication method, requested by the service provider, is supported");
396 throw new AuthenticationException(
397 "No authentication method, requested by the service provider, is supported");
398 }
399
400 return supportedLoginHandlers;
401 }
402
403
404
405
406
407
408
409
410
411
412
413
414
415 protected void filterByForceAuthentication(Session idpSession, LoginContext loginContext,
416 Map<String, LoginHandler> loginHandlers) throws ForceAuthenticationException {
417 LOG.debug("Forced authentication is required, filtering possible login handlers accordingly");
418
419 ArrayList<AuthenticationMethodInformation> activeMethods = new ArrayList<AuthenticationMethodInformation>();
420 if (idpSession != null) {
421 activeMethods.addAll(idpSession.getAuthenticationMethods().values());
422 }
423
424 loginHandlers.remove(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
425
426 LoginHandler loginHandler;
427 for (AuthenticationMethodInformation activeMethod : activeMethods) {
428 loginHandler = loginHandlers.get(activeMethod.getAuthenticationMethod());
429 if (loginHandler != null && !loginHandler.supportsForceAuthentication()) {
430 for (String handlerSupportedMethods : loginHandler.getSupportedAuthenticationMethods()) {
431 loginHandlers.remove(handlerSupportedMethods);
432 }
433 }
434 }
435
436 LOG.debug("Authentication handlers remaining after forced authentication requirement filtering: {}",
437 loginHandlers);
438
439 if (loginHandlers.isEmpty()) {
440 LOG.info("Force authentication requested but no login handlers available to support it");
441 throw new ForceAuthenticationException();
442 }
443 }
444
445
446
447
448
449
450
451
452
453
454
455 protected void filterByPassiveAuthentication(Session idpSession, LoginContext loginContext,
456 Map<String, LoginHandler> loginHandlers) throws PassiveAuthenticationException {
457 LOG.debug("Passive authentication is required, filtering poassible login handlers accordingly.");
458
459 if (idpSession == null) {
460 loginHandlers.remove(AuthnContext.PREVIOUS_SESSION_AUTHN_CTX);
461 }
462
463 LoginHandler loginHandler;
464 Iterator<Entry<String, LoginHandler>> authnMethodItr = loginHandlers.entrySet().iterator();
465 while (authnMethodItr.hasNext()) {
466 loginHandler = authnMethodItr.next().getValue();
467 if (!loginHandler.supportsPassive()) {
468 authnMethodItr.remove();
469 }
470 }
471
472 LOG.debug("Authentication handlers remaining after passive authentication requirement filtering: {}",
473 loginHandlers);
474
475 if (loginHandlers.isEmpty()) {
476 LOG.error("Passive authentication required but no login handlers available to support it");
477 throw new PassiveAuthenticationException();
478 }
479 }
480
481
482
483
484
485
486
487
488
489 protected void storeLoginContext(LoginContext loginContext, HttpServletRequest httpRequest,
490 HttpServletResponse httpResponse) {
491 String contextId = idGen.generateIdentifier();
492
493 storageService.put(loginContextPartitionName, contextId, new LoginContextEntry(loginContext,
494 loginContextEntryLifetime));
495
496 httpRequest.setAttribute(LOGIN_CONTEXT_KEY_NAME, contextId);
497
498 Cookie cookie = new Cookie(LOGIN_CONTEXT_KEY_NAME, contextId);
499 String contextPath = httpRequest.getContextPath();
500 if (DatatypeHelper.isEmpty(contextPath)) {
501 cookie.setPath("/");
502 } else {
503 cookie.setPath(contextPath);
504 }
505 cookie.setSecure(httpRequest.isSecure());
506 cookie.setMaxAge(-1);
507 httpResponse.addCookie(cookie);
508 }
509
510
511
512
513
514
515
516
517
518
519
520
521 protected void completeAuthentication(LoginContext loginContext, HttpServletRequest httpRequest,
522 HttpServletResponse httpResponse) {
523 LOG.debug("Completing user authentication process");
524
525 Session idpSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
526
527 try {
528
529
530 String actualAuthnMethod = DatatypeHelper.safeTrimOrNullString((String) httpRequest
531 .getAttribute(LoginHandler.AUTHENTICATION_METHOD_KEY));
532 if (actualAuthnMethod == null) {
533 actualAuthnMethod = loginContext.getAttemptedAuthnMethod();
534 } else {
535 LOG.debug("Authentication method overriden by LoginHandler. It was {} and is now {}", loginContext
536 .getAttemptedAuthnMethod(), actualAuthnMethod);
537 }
538
539
540 validateSuccessfulAuthentication(loginContext, httpRequest, actualAuthnMethod);
541
542
543
544 Subject subject = getLoginHandlerSubject(httpRequest);
545 if (loginContext.isForceAuthRequired()) {
546 validateForcedReauthentication(idpSession, actualAuthnMethod, subject);
547 }
548
549 loginContext.setPrincipalAuthenticated(true);
550 updateUserSession(loginContext, subject, actualAuthnMethod, httpRequest, httpResponse);
551 LOG.debug("User {} authenticated with method {}", loginContext.getPrincipalName(), loginContext
552 .getAuthenticationMethod());
553 } catch (AuthenticationException e) {
554 LOG.error("Authentication failed with the error:", e);
555 loginContext.setPrincipalAuthenticated(false);
556 loginContext.setAuthenticationFailure(e);
557 }
558
559 returnToProfileHandler(loginContext, httpRequest, httpResponse);
560 }
561
562
563
564
565
566
567
568
569
570
571
572
573
574 protected void validateSuccessfulAuthentication(LoginContext loginContext, HttpServletRequest httpRequest,
575 String authenticationMethod) throws AuthenticationException {
576 LOG.debug("Validating authentication was performed successfully");
577
578 String errorMessage = DatatypeHelper.safeTrimOrNullString((String) httpRequest
579 .getAttribute(LoginHandler.AUTHENTICATION_ERROR_KEY));
580 if (errorMessage != null) {
581 LOG.error("Error returned from login handler for authentication method {}:\n{}", loginContext
582 .getAttemptedAuthnMethod(), errorMessage);
583 throw new AuthenticationException(errorMessage);
584 }
585
586 Subject subject = (Subject) httpRequest.getAttribute(LoginHandler.SUBJECT_KEY);
587 Principal principal = (Principal) httpRequest.getAttribute(LoginHandler.PRINCIPAL_KEY);
588 String principalName = DatatypeHelper.safeTrimOrNullString((String) httpRequest
589 .getAttribute(LoginHandler.PRINCIPAL_NAME_KEY));
590
591 if (subject == null && principal == null && principalName == null) {
592 LOG.error("No user identified by login handler.");
593 throw new AuthenticationException("No user identified by login handler.");
594 }
595 }
596
597
598
599
600
601
602
603
604
605
606 protected Subject getLoginHandlerSubject(HttpServletRequest httpRequest) throws AuthenticationException {
607 Subject subject = (Subject) httpRequest.getAttribute(LoginHandler.SUBJECT_KEY);
608 Principal principal = (Principal) httpRequest.getAttribute(LoginHandler.PRINCIPAL_KEY);
609 String principalName = DatatypeHelper.safeTrimOrNullString((String) httpRequest
610 .getAttribute(LoginHandler.PRINCIPAL_NAME_KEY));
611
612 if (subject == null && (principal != null || principalName != null)) {
613 subject = new Subject();
614 if (principal == null) {
615 principal = new UsernamePrincipal(principalName);
616 }
617 subject.getPrincipals().add(principal);
618 }
619
620 return subject;
621 }
622
623
624
625
626
627
628
629
630
631
632
633
634 protected void validateForcedReauthentication(Session idpSession, String authnMethod, Subject subject)
635 throws AuthenticationException {
636 if (idpSession != null) {
637 AuthenticationMethodInformation authnMethodInfo = idpSession.getAuthenticationMethods().get(authnMethod);
638 if (authnMethodInfo != null) {
639 boolean princpalMatch = false;
640 for (Principal princpal : subject.getPrincipals()) {
641 if (authnMethodInfo.getAuthenticationPrincipal().equals(princpal)) {
642 princpalMatch = true;
643 break;
644 }
645 }
646
647 if (!princpalMatch) {
648 throw new ForceAuthenticationException(
649 "Authenticated principal does not match previously authenticated principal");
650 }
651 }
652 }
653 }
654
655
656
657
658
659
660
661
662
663
664
665 protected void updateUserSession(LoginContext loginContext, Subject authenticationSubject,
666 String authenticationMethod, HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
667 Principal authenticationPrincipal = authenticationSubject.getPrincipals().iterator().next();
668 LOG.debug("Updating session information for principal {}", authenticationPrincipal.getName());
669
670 Session idpSession = (Session) httpRequest.getAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE);
671 if (idpSession == null) {
672 LOG.debug("Creating shibboleth session for principal {}", authenticationPrincipal.getName());
673 idpSession = (Session) sessionManager.createSession();
674 loginContext.setSessionID(idpSession.getSessionID());
675 addSessionCookie(httpRequest, httpResponse, idpSession);
676 }
677
678
679
680 idpSession.setSubject(mergeSubjects(idpSession.getSubject(), authenticationSubject));
681
682 LOG.debug("Recording authentication and service information in Shibboleth session for principal: {}",
683 authenticationPrincipal.getName());
684 LoginHandler loginHandler = handlerManager.getLoginHandlers().get(loginContext.getAttemptedAuthnMethod());
685 AuthenticationMethodInformation authnMethodInfo = new AuthenticationMethodInformationImpl(idpSession
686 .getSubject(), authenticationPrincipal, authenticationMethod, new DateTime(), loginHandler
687 .getAuthenticationDuration());
688
689 loginContext.setAuthenticationMethodInformation(authnMethodInfo);
690 idpSession.getAuthenticationMethods().put(authnMethodInfo.getAuthenticationMethod(), authnMethodInfo);
691 sessionManager.indexSession(idpSession, authnMethodInfo.getAuthenticationPrincipal().getName());
692
693 ServiceInformation serviceInfo = new ServiceInformationImpl(loginContext.getRelyingPartyId(), new DateTime(),
694 authnMethodInfo);
695 idpSession.getServicesInformation().put(serviceInfo.getEntityID(), serviceInfo);
696 }
697
698
699
700
701
702
703
704
705
706 protected Subject mergeSubjects(Subject subject1, Subject subject2) {
707 if (subject1 == null) {
708 return subject2;
709 }
710
711 if (subject2 == null) {
712 return subject1;
713 }
714
715 if (subject1 == null && subject2 == null) {
716 return new Subject();
717 }
718
719 Set<Principal> principals = new HashSet<Principal>();
720 principals.addAll(subject1.getPrincipals());
721 principals.addAll(subject2.getPrincipals());
722
723 Set<Object> publicCredentials = new HashSet<Object>();
724 publicCredentials.addAll(subject1.getPublicCredentials());
725 publicCredentials.addAll(subject2.getPublicCredentials());
726
727 Set<Object> privateCredentials = new HashSet<Object>();
728 privateCredentials.addAll(subject1.getPrivateCredentials());
729 privateCredentials.addAll(subject2.getPrivateCredentials());
730
731 return new Subject(false, principals, publicCredentials, privateCredentials);
732 }
733
734
735
736
737
738
739
740
741 protected void addSessionCookie(HttpServletRequest httpRequest, HttpServletResponse httpResponse,
742 Session userSession) {
743 httpRequest.setAttribute(Session.HTTP_SESSION_BINDING_ATTRIBUTE, userSession);
744
745 byte[] remoteAddress = httpRequest.getRemoteAddr().getBytes();
746 byte[] sessionId = userSession.getSessionID().getBytes();
747
748 String signature = null;
749 try {
750 MessageDigest digester = MessageDigest.getInstance("SHA");
751 digester.update(userSession.getSessionSecret());
752 digester.update(remoteAddress);
753 digester.update(sessionId);
754 signature = Base64.encodeBytes(digester.digest());
755 } catch (GeneralSecurityException e) {
756 LOG.error("Unable to compute signature over session cookie material", e);
757 }
758
759 LOG.debug("Adding IdP session cookie to HTTP response");
760 StringBuilder cookieValue = new StringBuilder();
761 cookieValue.append(Base64.encodeBytes(remoteAddress, Base64.DONT_BREAK_LINES)).append("|");
762 cookieValue.append(Base64.encodeBytes(sessionId, Base64.DONT_BREAK_LINES)).append("|");
763 cookieValue.append(signature);
764 Cookie sessionCookie = new Cookie(IDP_SESSION_COOKIE_NAME, HTTPTransportUtils.urlEncode(cookieValue.toString()));
765
766 String contextPath = httpRequest.getContextPath();
767 if (DatatypeHelper.isEmpty(contextPath)) {
768 sessionCookie.setPath("/");
769 } else {
770 sessionCookie.setPath(contextPath);
771 }
772
773 sessionCookie.setSecure(httpRequest.isSecure());
774 sessionCookie.setMaxAge(-1);
775
776 httpResponse.addCookie(sessionCookie);
777 }
778 }