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      /** Unsolicited SSO indicator.  */
57      private boolean unsolicited;
58  
59      /**
60       * Creates a new instance of Saml2LoginContext.
61       * 
62       * @param relyingParty entity ID of the relying party
63       * @param state relay state from incoming authentication request
64       * @param request SAML 2.0 Authentication Request
65       * 
66       * @throws MarshallingException thrown if the given request can not be marshalled and serialized into a string
67       */
68      public Saml2LoginContext(String relyingParty, String state, AuthnRequest request) throws MarshallingException {
69          super();
70          
71          if (relyingParty == null || request == null) {
72              throw new IllegalArgumentException("SAML 2 authentication request and relying party ID may not be null");
73          }
74          setRelyingParty(relyingParty);
75          relayState = state;
76          serialAuthnRequest = serializeRequest(request);
77          
78          setForceAuthRequired(request.isForceAuthn());
79          setPassiveAuthRequired(request.isPassive());
80          getRequestedAuthenticationMethods().addAll(extractRequestedAuthenticationMethods(request));
81      }
82  
83      /**
84       * Gets the serialized authentication request that started the login process.
85       * 
86       * @return authentication request that started the login process
87       * 
88       * @throws UnmarshallingException thrown if the serialized form on the authentication request can be unmarshalled
89       */
90      public synchronized String getAuthenticationRequest() throws UnmarshallingException {
91          return serialAuthnRequest;
92      }
93      
94      /**
95       * Gets the relay state from the originating authentication request.
96       * 
97       * @return relay state from the originating authentication request
98       */
99      public synchronized String getRelayState(){
100         return relayState;
101     }
102 
103     /**
104      * Returns the unsolicited SSO indicator.
105      * 
106      * @return the unsolicited SSO indicator
107      */
108     public boolean isUnsolicited() {
109         return unsolicited;
110     }
111 
112     /**
113      * Sets the unsolicited SSO indicator.
114      * 
115      * @param unsolicited unsolicited SSO indicator to set
116      */
117     public void setUnsolicited(boolean unsolicited) {
118         this.unsolicited = unsolicited;
119     }        
120     
121     /**
122      * Serializes an authentication request into a string.
123      * 
124      * @param request the request to serialize
125      * 
126      * @return the serialized form of the string
127      * 
128      * @throws MarshallingException thrown if the request can not be marshalled and serialized
129      */
130     protected String serializeRequest(AuthnRequest request) throws MarshallingException {
131         Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(request);
132         Element requestElem = marshaller.marshall(request);
133         StringWriter writer = new StringWriter();
134         XMLHelper.writeNode(requestElem, writer);
135         return writer.toString();
136     }
137 
138     
139     /**
140      * Extracts the authentication methods requested within the request.
141      * 
142      * @param request the authentication request
143      * 
144      * @return requested authentication methods, or an empty list if no preference
145      */
146     protected List<String> extractRequestedAuthenticationMethods(AuthnRequest request){
147         LazyList<String> requestedMethods = new LazyList<String>();
148 
149         RequestedAuthnContext authnContext = request.getRequestedAuthnContext();
150         if (authnContext == null) {
151             return requestedMethods;
152         }
153 
154         // For the immediate future, we only support the "exact" comparator.
155         AuthnContextComparisonTypeEnumeration comparator = authnContext.getComparison();
156         if (comparator != null && comparator != AuthnContextComparisonTypeEnumeration.EXACT) {
157             Logger log = LoggerFactory.getLogger(Saml2LoginContext.class);
158             log.warn("Unsupported comparision operator ( " + comparator
159                     + ") in RequestedAuthnContext. Only exact comparisions are supported.");
160             return requestedMethods;
161         }
162 
163         // build a list of all requested authn classes and declrefs
164         List<AuthnContextClassRef> authnClasses = authnContext.getAuthnContextClassRefs();
165         if (authnClasses != null) {
166             for (AuthnContextClassRef classRef : authnClasses) {
167                 if (classRef != null && !DatatypeHelper.isEmpty(classRef.getAuthnContextClassRef())) {
168                     requestedMethods.add(classRef.getAuthnContextClassRef());
169                 }
170             }
171         }
172 
173         List<AuthnContextDeclRef> authnDeclRefs = authnContext.getAuthnContextDeclRefs();
174         if (authnDeclRefs != null) {
175             for (AuthnContextDeclRef declRef : authnDeclRefs) {
176                 if (declRef != null&& !DatatypeHelper.isEmpty(declRef.getAuthnContextDeclRef())) {
177                     requestedMethods.add(declRef.getAuthnContextDeclRef());
178                 }
179             }
180         }
181         
182         if(requestedMethods.contains(AuthnContext.UNSPECIFIED_AUTHN_CTX)){
183             requestedMethods.clear();
184         }
185 
186         return requestedMethods;
187     }
188 }