View Javadoc

1   /*
2    * Copyright 2006 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.authn;
18  
19  import java.io.Serializable;
20  import java.io.StringWriter;
21  import java.util.List;
22  
23  import org.opensaml.Configuration;
24  import org.opensaml.saml2.core.AuthnContext;
25  import org.opensaml.saml2.core.AuthnContextClassRef;
26  import org.opensaml.saml2.core.AuthnContextComparisonTypeEnumeration;
27  import org.opensaml.saml2.core.AuthnContextDeclRef;
28  import org.opensaml.saml2.core.AuthnRequest;
29  import org.opensaml.saml2.core.RequestedAuthnContext;
30  import org.opensaml.xml.io.Marshaller;
31  import org.opensaml.xml.io.MarshallingException;
32  import org.opensaml.xml.io.UnmarshallingException;
33  import org.opensaml.xml.util.DatatypeHelper;
34  import org.opensaml.xml.util.LazyList;
35  import org.opensaml.xml.util.XMLHelper;
36  import org.slf4j.Logger;
37  import org.slf4j.LoggerFactory;
38  import org.w3c.dom.Element;
39  
40  /**
41   * A SAML 2.0 {@link LoginContext}.
42   * 
43   * This class can interpret {@link RequestedAuthnContext} and act accordingly.
44   */
45  public class Saml2LoginContext extends LoginContext implements Serializable {
46  
47      /** Serial version UID. */
48      private static final long serialVersionUID = -7117092606828289070L;
49  
50      /** Relay state from authentication request. */
51      private String relayState;
52  
53      /** Serialized authentication request. */
54      private String serialAuthnRequest;
55  
56      /**
57       * Creates a new instance of Saml2LoginContext.
58       * 
59       * @param relyingParty entity ID of the relying party
60       * @param state relay state from incoming authentication request
61       * @param request SAML 2.0 Authentication Request
62       * 
63       * @throws MarshallingException thrown if the given request can not be marshalled and serialized into a string
64       */
65      public Saml2LoginContext(String relyingParty, String state, AuthnRequest request) throws MarshallingException {
66          super();
67          
68          if (relyingParty == null || request == null) {
69              throw new IllegalArgumentException("SAML 2 authentication request and relying party ID may not be null");
70          }
71          setRelyingParty(relyingParty);
72          relayState = state;
73          serialAuthnRequest = serializeRequest(request);
74          
75          setForceAuthRequired(request.isForceAuthn());
76          setPassiveAuthRequired(request.isPassive());
77          getRequestedAuthenticationMethods().addAll(extractRequestedAuthenticationMethods(request));
78      }
79  
80      /**
81       * Gets the serialized authentication request that started the login process.
82       * 
83       * @return authentication request that started the login process
84       * 
85       * @throws UnmarshallingException thrown if the serialized form on the authentication request can be unmarshalled
86       */
87      public synchronized String getAuthenticationRequest() throws UnmarshallingException {
88          return serialAuthnRequest;
89      }
90      
91      /**
92       * Gets the relay state from the originating authentication request.
93       * 
94       * @return relay state from the originating authentication request
95       */
96      public synchronized String getRelayState(){
97          return relayState;
98      }
99  
100     /**
101      * Serializes an authentication request into a string.
102      * 
103      * @param request the request to serialize
104      * 
105      * @return the serialized form of the string
106      * 
107      * @throws MarshallingException thrown if the request can not be marshalled and serialized
108      */
109     protected String serializeRequest(AuthnRequest request) throws MarshallingException {
110         Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(request);
111         Element requestElem = marshaller.marshall(request);
112         StringWriter writer = new StringWriter();
113         XMLHelper.writeNode(requestElem, writer);
114         return writer.toString();
115     }
116 
117     
118     /**
119      * Extracts the authentication methods requested within the request.
120      * 
121      * @param request the authentication request
122      * 
123      * @return requested authentication methods, or an empty list if no preference
124      */
125     protected List<String> extractRequestedAuthenticationMethods(AuthnRequest request){
126         LazyList<String> requestedMethods = new LazyList<String>();
127 
128         RequestedAuthnContext authnContext = request.getRequestedAuthnContext();
129         if (authnContext == null) {
130             return requestedMethods;
131         }
132 
133         // For the immediate future, we only support the "exact" comparator.
134         AuthnContextComparisonTypeEnumeration comparator = authnContext.getComparison();
135         if (comparator != null && comparator != AuthnContextComparisonTypeEnumeration.EXACT) {
136             Logger log = LoggerFactory.getLogger(Saml2LoginContext.class);
137             log.warn("Unsupported comparision operator ( " + comparator
138                     + ") in RequestedAuthnContext. Only exact comparisions are supported.");
139             return requestedMethods;
140         }
141 
142         // build a list of all requested authn classes and declrefs
143         List<AuthnContextClassRef> authnClasses = authnContext.getAuthnContextClassRefs();
144         if (authnClasses != null) {
145             for (AuthnContextClassRef classRef : authnClasses) {
146                 if (classRef != null && !DatatypeHelper.isEmpty(classRef.getAuthnContextClassRef())) {
147                     requestedMethods.add(classRef.getAuthnContextClassRef());
148                 }
149             }
150         }
151 
152         List<AuthnContextDeclRef> authnDeclRefs = authnContext.getAuthnContextDeclRefs();
153         if (authnDeclRefs != null) {
154             for (AuthnContextDeclRef declRef : authnDeclRefs) {
155                 if (declRef != null&& !DatatypeHelper.isEmpty(declRef.getAuthnContextDeclRef())) {
156                     requestedMethods.add(declRef.getAuthnContextDeclRef());
157                 }
158             }
159         }
160         
161         if(requestedMethods.contains(AuthnContext.UNSPECIFIED_AUTHN_CTX)){
162             requestedMethods.clear();
163         }
164 
165         return requestedMethods;
166     }
167 }