Fix bug SIDP-58
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / idp / profile / saml2 / SSOProfileHandler.java
1 /*
2  * Copyright [2007] [University Corporation for Advanced Internet Development, Inc.]
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 package edu.internet2.middleware.shibboleth.idp.profile.saml2;
18
19 import java.io.IOException;
20 import java.util.ArrayList;
21
22 import javax.servlet.RequestDispatcher;
23 import javax.servlet.ServletException;
24 import javax.servlet.http.HttpServletRequest;
25 import javax.servlet.http.HttpSession;
26
27 import org.opensaml.common.SAMLObjectBuilder;
28 import org.opensaml.common.binding.decoding.SAMLMessageDecoder;
29 import org.opensaml.common.xml.SAMLConstants;
30 import org.opensaml.saml2.binding.AuthnResponseEndpointSelector;
31 import org.opensaml.saml2.core.AttributeStatement;
32 import org.opensaml.saml2.core.AuthnContext;
33 import org.opensaml.saml2.core.AuthnContextClassRef;
34 import org.opensaml.saml2.core.AuthnContextDeclRef;
35 import org.opensaml.saml2.core.AuthnRequest;
36 import org.opensaml.saml2.core.AuthnStatement;
37 import org.opensaml.saml2.core.RequestedAuthnContext;
38 import org.opensaml.saml2.core.Response;
39 import org.opensaml.saml2.core.Statement;
40 import org.opensaml.saml2.core.StatusCode;
41 import org.opensaml.saml2.core.SubjectLocality;
42 import org.opensaml.saml2.metadata.AssertionConsumerService;
43 import org.opensaml.saml2.metadata.Endpoint;
44 import org.opensaml.saml2.metadata.EntityDescriptor;
45 import org.opensaml.saml2.metadata.IDPSSODescriptor;
46 import org.opensaml.saml2.metadata.SPSSODescriptor;
47 import org.opensaml.saml2.metadata.provider.MetadataProvider;
48 import org.opensaml.saml2.metadata.provider.MetadataProviderException;
49 import org.opensaml.ws.message.decoder.MessageDecodingException;
50 import org.opensaml.ws.transport.http.HTTPInTransport;
51 import org.opensaml.ws.transport.http.HTTPOutTransport;
52 import org.opensaml.ws.transport.http.HttpServletRequestAdapter;
53 import org.opensaml.ws.transport.http.HttpServletResponseAdapter;
54 import org.opensaml.xml.io.MarshallingException;
55 import org.opensaml.xml.io.UnmarshallingException;
56 import org.opensaml.xml.security.SecurityException;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 import edu.internet2.middleware.shibboleth.common.profile.ProfileException;
61 import edu.internet2.middleware.shibboleth.common.relyingparty.ProfileConfiguration;
62 import edu.internet2.middleware.shibboleth.common.relyingparty.RelyingPartyConfiguration;
63 import edu.internet2.middleware.shibboleth.common.relyingparty.provider.saml2.SSOConfiguration;
64 import edu.internet2.middleware.shibboleth.common.util.HttpHelper;
65 import edu.internet2.middleware.shibboleth.idp.authn.LoginContext;
66 import edu.internet2.middleware.shibboleth.idp.authn.Saml2LoginContext;
67
68 /** SAML 2.0 SSO request profile handler. */
69 public class SSOProfileHandler extends AbstractSAML2ProfileHandler {
70
71     /** Class logger. */
72     private final Logger log = LoggerFactory.getLogger(SSOProfileHandler.class);
73
74     /** Builder of AuthnStatement objects. */
75     private SAMLObjectBuilder<AuthnStatement> authnStatementBuilder;
76
77     /** Builder of AuthnContext objects. */
78     private SAMLObjectBuilder<AuthnContext> authnContextBuilder;
79
80     /** Builder of AuthnContextClassRef objects. */
81     private SAMLObjectBuilder<AuthnContextClassRef> authnContextClassRefBuilder;
82
83     /** Builder of AuthnContextDeclRef objects. */
84     private SAMLObjectBuilder<AuthnContextDeclRef> authnContextDeclRefBuilder;
85
86     /** Builder of SubjectLocality objects. */
87     private SAMLObjectBuilder<SubjectLocality> subjectLocalityBuilder;
88
89     /** URL of the authentication manager servlet. */
90     private String authenticationManagerPath;
91
92     /** URI of request decoder. */
93     private String decodingBinding;
94
95     /**
96      * Constructor.
97      * 
98      * @param authnManagerPath path to the authentication manager servlet
99      */
100     @SuppressWarnings("unchecked")
101     public SSOProfileHandler(String authnManagerPath) {
102         super();
103
104         authenticationManagerPath = authnManagerPath;
105
106         authnStatementBuilder = (SAMLObjectBuilder<AuthnStatement>) getBuilderFactory().getBuilder(
107                 AuthnStatement.DEFAULT_ELEMENT_NAME);
108         authnContextBuilder = (SAMLObjectBuilder<AuthnContext>) getBuilderFactory().getBuilder(
109                 AuthnContext.DEFAULT_ELEMENT_NAME);
110         authnContextClassRefBuilder = (SAMLObjectBuilder<AuthnContextClassRef>) getBuilderFactory().getBuilder(
111                 AuthnContextClassRef.DEFAULT_ELEMENT_NAME);
112         authnContextDeclRefBuilder = (SAMLObjectBuilder<AuthnContextDeclRef>) getBuilderFactory().getBuilder(
113                 AuthnContextDeclRef.DEFAULT_ELEMENT_NAME);
114         subjectLocalityBuilder = (SAMLObjectBuilder<SubjectLocality>) getBuilderFactory().getBuilder(
115                 SubjectLocality.DEFAULT_ELEMENT_NAME);
116     }
117
118     /** {@inheritDoc} */
119     public String getProfileId() {
120         return "urn:mace:shibboleth:2.0:idp:profiles:saml2:request:sso";
121     }
122
123     /** {@inheritDoc} */
124     public void processRequest(HTTPInTransport inTransport, HTTPOutTransport outTransport) throws ProfileException {
125         HttpServletRequest servletRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
126         HttpSession httpSession = servletRequest.getSession(true);
127         LoginContext loginContext = (LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
128
129         if (loginContext == null) {
130             log.debug("User session does not contain a login context, processing as first leg of request");
131             performAuthentication(inTransport, outTransport);
132         }else if (!loginContext.isPrincipalAuthenticated()){
133             log.debug("User session contained a login context but user was not authenticated, processing as first leg of request");
134             performAuthentication(inTransport, outTransport);
135         } else {
136             log.debug("User session contains a login context, processing as second leg of request");
137             completeAuthenticationRequest(inTransport, outTransport);
138         }
139     }
140
141     /**
142      * Creates a {@link Saml2LoginContext} an sends the request off to the AuthenticationManager to begin the process of
143      * authenticating the user.
144      * 
145      * @param inTransport inbound request transport
146      * @param outTransport outbound response transport
147      * 
148      * @throws ProfileException thrown if there is a problem creating the login context and transferring control to the
149      *             authentication manager
150      */
151     protected void performAuthentication(HTTPInTransport inTransport, HTTPOutTransport outTransport)
152             throws ProfileException {
153         HttpServletRequest servletRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
154         HttpSession httpSession = servletRequest.getSession();
155
156         try {
157             SSORequestContext requestContext = decodeRequest(inTransport, outTransport);
158
159             String relyingPartyId = requestContext.getInboundMessageIssuer();
160             RelyingPartyConfiguration rpConfig = getRelyingPartyConfiguration(relyingPartyId);
161             ProfileConfiguration ssoConfig = rpConfig.getProfileConfiguration(SSOConfiguration.PROFILE_ID);
162             if (ssoConfig == null) {
163                 log.error("SAML 2 SSO profile is not configured for relying party "
164                         + requestContext.getInboundMessageIssuer());
165                 throw new ProfileException("SAML 2 SSO profile is not configured for relying party "
166                         + requestContext.getInboundMessageIssuer());
167             }
168
169             log.debug("Creating login context and transferring control to authentication engine");
170             Saml2LoginContext loginContext = new Saml2LoginContext(relyingPartyId, requestContext.getRelayState(),
171                     requestContext.getInboundSAMLMessage());
172             loginContext.setAuthenticationEngineURL(authenticationManagerPath);
173             loginContext.setProfileHandlerURL(HttpHelper.getRequestUriWithoutContext(servletRequest));
174             if (loginContext.getRequestedAuthenticationMethods().size() == 0) {
175                 loginContext.getRequestedAuthenticationMethods().add(rpConfig.getDefaultAuthenticationMethod());
176             }
177             
178             httpSession.setAttribute(Saml2LoginContext.LOGIN_CONTEXT_KEY, loginContext);
179             RequestDispatcher dispatcher = servletRequest.getRequestDispatcher(authenticationManagerPath);
180             dispatcher.forward(servletRequest, ((HttpServletResponseAdapter) outTransport).getWrappedResponse());
181         } catch (MarshallingException e) {
182             httpSession.removeAttribute(LoginContext.LOGIN_CONTEXT_KEY);
183             log.error("Unable to marshall authentication request context");
184             throw new ProfileException("Unable to marshall authentication request context", e);
185         } catch (IOException ex) {
186             httpSession.removeAttribute(LoginContext.LOGIN_CONTEXT_KEY);
187             log.error("Error forwarding SAML 2 AuthnRequest to AuthenticationManager", ex);
188             throw new ProfileException("Error forwarding SAML 2 AuthnRequest to AuthenticationManager", ex);
189         } catch (ServletException ex) {
190             httpSession.removeAttribute(LoginContext.LOGIN_CONTEXT_KEY);
191             log.error("Error forwarding SAML 2 AuthnRequest to AuthenticationManager", ex);
192             throw new ProfileException("Error forwarding SAML 2 AuthnRequest to AuthenticationManager", ex);
193         }
194     }
195
196     /**
197      * Creates a response to the {@link AuthnRequest} and sends the user, with response in tow, back to the relying
198      * party after they've been authenticated.
199      * 
200      * @param inTransport inbound message transport
201      * @param outTransport outbound message transport
202      * 
203      * @throws ProfileException thrown if the response can not be created and sent back to the relying party
204      */
205     protected void completeAuthenticationRequest(HTTPInTransport inTransport, HTTPOutTransport outTransport)
206             throws ProfileException {
207         HttpServletRequest servletRequest = ((HttpServletRequestAdapter) inTransport).getWrappedRequest();
208         HttpSession httpSession = servletRequest.getSession();
209
210         Saml2LoginContext loginContext = (Saml2LoginContext) httpSession.getAttribute(LoginContext.LOGIN_CONTEXT_KEY);
211         httpSession.removeAttribute(LoginContext.LOGIN_CONTEXT_KEY);
212
213         SSORequestContext requestContext = buildRequestContext(loginContext, inTransport, outTransport);
214
215         checkSamlVersion(requestContext);
216
217         Response samlResponse;
218         try {
219             if (loginContext.getPrincipalName() == null) {
220                 log.error("User's login context did not contain a principal, user considered unauthenticiated.");
221                 requestContext
222                         .setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, StatusCode.AUTHN_FAILED_URI, null));
223                 throw new ProfileException("User failed authentication");
224             }
225
226             resolveAttributes(requestContext);
227
228             ArrayList<Statement> statements = new ArrayList<Statement>();
229             statements.add(buildAuthnStatement(requestContext));
230             if (requestContext.getProfileConfiguration().includeAttributeStatement()){
231                 AttributeStatement attributeStatement = buildAttributeStatement(requestContext);
232                 if(attributeStatement != null){
233                     requestContext.setRequestedAttributes(requestContext.getPrincipalAttributes().keySet());
234                     statements.add(attributeStatement);
235                 }
236             }
237
238             samlResponse = buildResponse(requestContext, "urn:oasis:names:tc:SAML:2.0:cm:bearer", statements);
239         } catch (ProfileException e) {
240             samlResponse = buildErrorResponse(requestContext);
241         }
242
243         requestContext.setOutboundSAMLMessage(samlResponse);
244         requestContext.setOutboundSAMLMessageId(samlResponse.getID());
245         requestContext.setOutboundSAMLMessageIssueInstant(samlResponse.getIssueInstant());
246         encodeResponse(requestContext);
247         writeAuditLogEntry(requestContext);
248     }
249
250     /**
251      * Decodes an incoming request and stores the information in a created request context.
252      * 
253      * @param inTransport inbound transport
254      * @param outTransport outbound transport
255      * 
256      * @return request context with decoded information
257      * 
258      * @throws ProfileException thrown if the incomming message failed decoding
259      */
260     protected SSORequestContext decodeRequest(HTTPInTransport inTransport, HTTPOutTransport outTransport)
261             throws ProfileException {
262         log.debug("Decoding message with decoder binding {}", decodingBinding);
263
264         SSORequestContext requestContext = new SSORequestContext();
265         requestContext.setMetadataProvider(getMetadataProvider());
266         requestContext.setSecurityPolicyResolver(getSecurityPolicyResolver());
267
268         requestContext.setCommunicationProfileId(SSOConfiguration.PROFILE_ID);
269         requestContext.setInboundMessageTransport(inTransport);
270         requestContext.setInboundSAMLProtocol(SAMLConstants.SAML20P_NS);
271         requestContext.setPeerEntityRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
272
273         requestContext.setOutboundMessageTransport(outTransport);
274         requestContext.setOutboundSAMLProtocol(SAMLConstants.SAML20P_NS);
275
276         try {
277             SAMLMessageDecoder decoder = getMessageDecoders().get(getInboundBinding());
278             requestContext.setMessageDecoder(decoder);
279             decoder.decode(requestContext);
280             return requestContext;
281         } catch (MessageDecodingException e) {
282             log.error("Error decoding authentication request message", e);
283             throw new ProfileException("Error decoding authentication request message", e);
284         } catch (SecurityException e) {
285             log.error("Message did not meet security requirements", e);
286             throw new ProfileException("Message did not meet security requirements", e);
287         }
288     }
289
290     /**
291      * Creates an authentication request context from the current environmental information.
292      * 
293      * @param loginContext current login context
294      * @param in inbound transport
295      * @param out outbount transport
296      * 
297      * @return created authentication request context
298      * 
299      * @throws ProfileException thrown if there is a problem creating the context
300      */
301     protected SSORequestContext buildRequestContext(Saml2LoginContext loginContext, HTTPInTransport in,
302             HTTPOutTransport out) throws ProfileException {
303         SSORequestContext requestContext = new SSORequestContext();
304
305         requestContext.setMessageDecoder(getMessageDecoders().get(getInboundBinding()));
306
307         requestContext.setLoginContext(loginContext);
308         requestContext.setPrincipalName(loginContext.getPrincipalName());
309         requestContext.setPrincipalAuthenticationMethod(loginContext.getAuthenticationMethod());
310         requestContext.setUserSession(getUserSession(in));
311         requestContext.setRelayState(loginContext.getRelayState());
312
313         requestContext.setInboundMessageTransport(in);
314         requestContext.setInboundSAMLProtocol(SAMLConstants.SAML20P_NS);
315
316         try {
317             requestContext.setInboundMessage(loginContext.getAuthenticationRequest());
318             requestContext.setInboundSAMLMessage(loginContext.getAuthenticationRequest());
319             requestContext.setInboundSAMLMessageId(loginContext.getAuthenticationRequest().getID());
320         } catch (UnmarshallingException e) {
321             log.error("Unable to unmarshall authentication request context");
322             requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
323                     "Error recovering request state"));
324             throw new ProfileException("Error recovering request state", e);
325         }
326
327         requestContext.setOutboundMessageTransport(out);
328         requestContext.setOutboundSAMLProtocol(SAMLConstants.SAML20P_NS);
329
330         MetadataProvider metadataProvider = getMetadataProvider();
331         requestContext.setMetadataProvider(metadataProvider);
332
333         String relyingPartyId = loginContext.getRelyingPartyId();
334         requestContext.setInboundMessageIssuer(relyingPartyId);
335         try {
336             EntityDescriptor relyingPartyMetadata = metadataProvider.getEntityDescriptor(relyingPartyId);
337             if (relyingPartyMetadata != null) {
338                 requestContext.setPeerEntityMetadata(relyingPartyMetadata);
339                 requestContext.setPeerEntityRole(SPSSODescriptor.DEFAULT_ELEMENT_NAME);
340                 requestContext.setPeerEntityRoleMetadata(relyingPartyMetadata
341                         .getSPSSODescriptor(SAMLConstants.SAML20P_NS));
342             }
343         } catch (MetadataProviderException e) {
344             log.error("Unable to locate metadata for relying party");
345             requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
346                     "Error locating relying party metadata"));
347             throw new ProfileException("Error locating relying party metadata");
348         }
349
350         RelyingPartyConfiguration rpConfig = getRelyingPartyConfiguration(relyingPartyId);
351         if (rpConfig == null) {
352             log.error("Unable to retrieve relying party configuration data for entity with ID {}", relyingPartyId);
353             throw new ProfileException("Unable to retrieve relying party configuration data for entity with ID "
354                     + relyingPartyId);
355         }
356         requestContext.setRelyingPartyConfiguration(rpConfig);
357
358         SSOConfiguration profileConfig = (SSOConfiguration) rpConfig
359                 .getProfileConfiguration(SSOConfiguration.PROFILE_ID);
360         requestContext.setProfileConfiguration(profileConfig);
361         requestContext.setOutboundMessageArtifactType(profileConfig.getOutboundArtifactType());
362         if (profileConfig.getSigningCredential() != null) {
363             requestContext.setOutboundSAMLMessageSigningCredential(profileConfig.getSigningCredential());
364         } else if (rpConfig.getDefaultSigningCredential() != null) {
365             requestContext.setOutboundSAMLMessageSigningCredential(rpConfig.getDefaultSigningCredential());
366         }
367         requestContext.setPeerEntityEndpoint(selectEndpoint(requestContext));
368
369         String assertingPartyId = rpConfig.getProviderId();
370         requestContext.setLocalEntityId(assertingPartyId);
371
372         try {
373             EntityDescriptor localEntityDescriptor = metadataProvider.getEntityDescriptor(assertingPartyId);
374             if (localEntityDescriptor != null) {
375                 requestContext.setLocalEntityMetadata(localEntityDescriptor);
376                 requestContext.setLocalEntityRole(IDPSSODescriptor.DEFAULT_ELEMENT_NAME);
377                 requestContext.setLocalEntityRoleMetadata(localEntityDescriptor
378                         .getIDPSSODescriptor(SAMLConstants.SAML20P_NS));
379             }
380         } catch (MetadataProviderException e) {
381             log.error("Unable to locate metadata for asserting party");
382             requestContext.setFailureStatus(buildStatus(StatusCode.RESPONDER_URI, null,
383                     "Error locating asserting party metadata"));
384             throw new ProfileException("Error locating asserting party metadata");
385         }
386
387         return requestContext;
388     }
389
390     /**
391      * Creates an authentication statement for the current request.
392      * 
393      * @param requestContext current request context
394      * 
395      * @return constructed authentication statement
396      */
397     protected AuthnStatement buildAuthnStatement(SSORequestContext requestContext) {
398         Saml2LoginContext loginContext = requestContext.getLoginContext();
399
400         AuthnContext authnContext = buildAuthnContext(requestContext);
401
402         AuthnStatement statement = authnStatementBuilder.buildObject();
403         statement.setAuthnContext(authnContext);
404         statement.setAuthnInstant(loginContext.getAuthenticationInstant());
405
406         // TODO
407         statement.setSessionIndex(null);
408
409         if (loginContext.getAuthenticationDuration() > 0) {
410             statement.setSessionNotOnOrAfter(loginContext.getAuthenticationInstant().plus(
411                     loginContext.getAuthenticationDuration()));
412         }
413
414         statement.setSubjectLocality(buildSubjectLocality(requestContext));
415
416         return statement;
417     }
418
419     /**
420      * Creates an {@link AuthnContext} for a succesful authentication request.
421      * 
422      * @param requestContext current request
423      * 
424      * @return the built authn context
425      */
426     protected AuthnContext buildAuthnContext(SSORequestContext requestContext) {
427         AuthnContext authnContext = authnContextBuilder.buildObject();
428
429         Saml2LoginContext loginContext = requestContext.getLoginContext();
430         AuthnRequest authnRequest = requestContext.getInboundSAMLMessage();
431         RequestedAuthnContext requestedAuthnContext = authnRequest.getRequestedAuthnContext();
432         if (requestedAuthnContext != null) {
433             if (requestedAuthnContext.getAuthnContextClassRefs() != null) {
434                 for (AuthnContextClassRef classRef : requestedAuthnContext.getAuthnContextClassRefs()) {
435                     if (classRef.getAuthnContextClassRef().equals(loginContext.getAuthenticationMethod())) {
436                         AuthnContextClassRef ref = authnContextClassRefBuilder.buildObject();
437                         ref.setAuthnContextClassRef(loginContext.getAuthenticationMethod());
438                         authnContext.setAuthnContextClassRef(ref);
439                     }
440                 }
441             } else if (requestedAuthnContext.getAuthnContextDeclRefs() != null) {
442                 for (AuthnContextDeclRef declRef : requestedAuthnContext.getAuthnContextDeclRefs()) {
443                     if (declRef.getAuthnContextDeclRef().equals(loginContext.getAuthenticationMethod())) {
444                         AuthnContextDeclRef ref = authnContextDeclRefBuilder.buildObject();
445                         ref.setAuthnContextDeclRef(loginContext.getAuthenticationMethod());
446                         authnContext.setAuthnContextDeclRef(ref);
447                     }
448                 }
449             }
450         } else {
451             AuthnContextDeclRef ref = authnContextDeclRefBuilder.buildObject();
452             ref.setAuthnContextDeclRef(loginContext.getAuthenticationMethod());
453             authnContext.setAuthnContextDeclRef(ref);
454         }
455
456         return authnContext;
457     }
458
459     /**
460      * Constructs the subject locality for the authentication statement.
461      * 
462      * @param requestContext curent request context
463      * 
464      * @return subject locality for the authentication statement
465      */
466     protected SubjectLocality buildSubjectLocality(SSORequestContext requestContext) {
467         HTTPInTransport transport = (HTTPInTransport) requestContext.getInboundMessageTransport();
468         SubjectLocality subjectLocality = subjectLocalityBuilder.buildObject();
469         subjectLocality.setAddress(transport.getPeerAddress());
470         subjectLocality.setDNSName(transport.getPeerDomainName());
471
472         return subjectLocality;
473     }
474
475     /**
476      * Selects the appropriate endpoint for the relying party and stores it in the request context.
477      * 
478      * @param requestContext current request context
479      * 
480      * @return Endpoint selected from the information provided in the request context
481      */
482     protected Endpoint selectEndpoint(SSORequestContext requestContext) {
483         AuthnResponseEndpointSelector endpointSelector = new AuthnResponseEndpointSelector();
484         endpointSelector.setEndpointType(AssertionConsumerService.DEFAULT_ELEMENT_NAME);
485         endpointSelector.setMetadataProvider(getMetadataProvider());
486         endpointSelector.setEntityMetadata(requestContext.getPeerEntityMetadata());
487         endpointSelector.setEntityRoleMetadata(requestContext.getPeerEntityRoleMetadata());
488         endpointSelector.setSamlRequest(requestContext.getInboundSAMLMessage());
489         endpointSelector.getSupportedIssuerBindings().addAll(getSupportedOutboundBindings());
490         return endpointSelector.selectEndpoint();
491     }
492
493     /** Represents the internal state of a SAML 2.0 SSO Request while it's being processed by the IdP. */
494     protected class SSORequestContext extends BaseSAML2ProfileRequestContext<AuthnRequest, Response, SSOConfiguration> {
495
496         /** Current login context. */
497         private Saml2LoginContext loginContext;
498
499         /**
500          * Gets the current login context.
501          * 
502          * @return current login context
503          */
504         public Saml2LoginContext getLoginContext() {
505             return loginContext;
506         }
507
508         /**
509          * Sets the current login context.
510          * 
511          * @param context current login context
512          */
513         public void setLoginContext(Saml2LoginContext context) {
514             loginContext = context;
515         }
516     }
517 }