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