a928f3d2f7586909cbfdaf5463e5b1743d05d277
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / serviceprovider / AssertionConsumerServlet.java
1 /*
2  * Copyright [2005] [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 /*
18  * AuthenticatonAssertionConsumerServlet
19  * 
20  * The Shibboleth function previous known as the SHIRE.
21  * 
22  * Authentication Assertion Consumer is the SAML 2.0 term for what the
23  * SHIRE does. A SAML Assertion containing an Authentication statement
24  * with the "principal" identifier value equal to the handle vended by
25  * the Handle Server is received from the Browser. The Handle Server
26  * generated a form, prefilled it with a Bin64 encoding of the SAML
27  * statement, and included Javascript to automatically submit the form
28  * to this URL.
29  * 
30  * All HTTP, HTML, and servlet logic is localized to this layer of
31  * modules. Any information must be extracted from the Servlet API
32  * to be passed directly to Shibboleth.
33  * 
34  * The work is done by a ShibPOSTProfile object. It takes the Bin64
35  * encoded string, converts it to a SAMLObject, verifies structure,
36  * and validates signatures.
37  * 
38  * The resulting Authentication Assertion SAML statement is passed
39  * to Session Manager to create a new session. This process feeds
40  * back a session identifier that becomes the value of a Cookie sent
41  * back to the Browser to track the session.
42  * 
43  * If the decision is made to fetch attributes immediately, the 
44  * Session object can be passed to the static AttributeRequestor
45  * service. It generates a ShibBinding, sends a request to the AA,
46  * validates the response, applies AAP, and stores the resulting 
47  * SAML Attribute Assertion in the Session object.
48  */
49 package edu.internet2.middleware.shibboleth.serviceprovider;
50
51 import java.io.IOException;
52 import java.util.Iterator;
53
54 import javax.servlet.ServletContext;
55 import javax.servlet.ServletException;
56 import javax.servlet.ServletOutputStream;
57 import javax.servlet.http.Cookie;
58 import javax.servlet.http.HttpServlet;
59 import javax.servlet.http.HttpServletRequest;
60 import javax.servlet.http.HttpServletResponse;
61
62 import org.apache.log4j.Logger;
63 import org.opensaml.SAMLAudienceRestrictionCondition;
64 import org.opensaml.SAMLCondition;
65 import org.opensaml.SAMLException;
66 import org.opensaml.SAMLResponse;
67 import org.opensaml.SAMLBrowserProfile.BrowserProfileResponse;
68 import x0.maceShibbolethTargetConfig1.SessionsDocument.Sessions;
69 import edu.internet2.middleware.shibboleth.common.ShibBrowserProfile;
70 import edu.internet2.middleware.shibboleth.metadata.MetadataException;
71 import edu.internet2.middleware.shibboleth.resource.AuthenticationFilter;
72 import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderConfig.ApplicationInfo;
73
74 /**
75  * Process the Authentication Assertion POST data to create a Session.
76  * 
77  * @author Howard Gilbert
78  */
79 public class AssertionConsumerServlet extends HttpServlet {
80
81         private static final String SESSIONCOOKIE = "ShibbolethSPSession";
82
83         private static Logger log = Logger.getLogger(AssertionConsumerServlet.class.getName());
84         
85         private static ServiceProviderContext context = ServiceProviderContext.getInstance();
86         
87         public static final String SESSIONPARM =
88             "ShibbolethSessionId";
89         
90         
91         public void init() throws ServletException {
92                 super.init();
93                 ServletContext servletContext = this.getServletContext();
94                 
95                 // Note: the ServletContext should have been initialized by the Listener
96                 ServletContextInitializer.initServiceProvider(servletContext);
97                 
98                 // Establish linkage between the SP context and the RM Filter class
99                 AuthenticationFilter.setFilterSupport(new FilterSupportImpl());
100         }
101
102
103
104         /**
105          * Accept the SAML Assertion post from the HS.
106          * 
107          * @param request the request send by the client to the server
108          * @param response the response send by the server to the client
109          */
110         public void doPost(
111                 HttpServletRequest request,
112                 HttpServletResponse response)
113                 {
114             ServletContextInitializer.beginService(request,response);
115                 try {
116             ServiceProviderConfig config = context.getServiceProviderConfig();
117             
118             String ipaddr = request.getRemoteAddr();
119             
120             // URL of Resource that triggered authorization
121             // I added support to the profile for extracting TARGET, but
122             // it's not too critical in Java since you can grab it easily anyway.
123             // Might be better in the 2.0 future though, since the bindings get trickier.
124             String target = request.getParameter("TARGET");
125             
126             // Map the Resource URL into an <Application>
127             String applicationId = config.mapRequest(target);
128             ApplicationInfo appinfo = config.getApplication(applicationId);
129             Sessions appSessionValues = appinfo.getApplicationConfig().getSessions();
130             String shireURL = request.getRequestURL().toString();
131             String providerId = appinfo.getApplicationConfig().getProviderId();
132             
133            
134             if (appSessionValues.getShireSSL()&& // Requires SSL
135                         !request.isSecure()) {       // isn't SSL
136                 log.error("Authentication Assertion not posted over SSL.");
137                 try {
138                     response.sendRedirect("shireError.html");
139                 } catch (IOException e1) {
140                 }
141                 return;
142             }
143             
144             log.debug("Authentication received from "+ipaddr+" for "+target+
145                         "(application:"+applicationId+") (Provider:"+providerId+")");
146
147             String sessionId = createSessionFromPost(ipaddr, request, applicationId, shireURL, providerId, null);
148             
149             Cookie cookie = new Cookie(SESSIONCOOKIE,sessionId);
150             response.addCookie(cookie);
151             
152             /**
153              * This is included as a diagnostic, although it was suggested by a user
154              * as a future API for someone holding an Authentication and wishing to
155              * know what it means.
156              */
157             try {
158                                 if (target.equals("SendAttributesBackToMe")) {
159                                         ServletOutputStream outputStream = response.getOutputStream();
160                                         response.setContentType("text/xml");
161                                         Session session = context.getSessionManager().findSession(sessionId,applicationId);
162                                         SAMLResponse attributeResponse = session.getAttributeResponse();
163                                         outputStream.print(attributeResponse.toString());
164                                 } else {
165                                         response.sendRedirect(target+"?"+SESSIONPARM+"="+sessionId);
166                                 }
167             }
168             catch (IOException e) {
169             }
170         }
171         catch (MetadataException e) {
172             log.error("Authentication Assertion source not found in Metadata.");
173             try {
174                 response.sendRedirect("shireError.html");
175             }
176             catch (IOException e1) {
177             }
178         }
179         catch (SAMLException e) {
180             log.error("Authentication Assertion had invalid format.");
181             try {
182                 response.sendRedirect("shireError.html");
183             }
184             catch (IOException e1) {
185             }
186         }
187         finally {
188             ServletContextInitializer.finishService(request,response);
189         }
190         }
191         
192     /**
193      * Create a Session object from SHIRE POST data
194      * 
195      * <p>Used within this class to handle POSTs to the SP itself, but
196      * also by the FilterSupport logic when the POST is to the RM 
197      * context.</p>
198      * 
199      * @param ipaddr IP Address of Browser
200      * @param bin64Assertion Authentication assertion from POST
201      * @param applicationId from RequestMap
202      * @param shireURL 
203      * @param providerId Our Entity name
204      * @return random key of Session
205      * @throws SAMLException
206      */
207     public static 
208     String createSessionFromPost(
209             String ipaddr, 
210             HttpServletRequest req, 
211             String applicationId, 
212             String shireURL, 
213             String providerId,
214             String emptySessionId
215             ) 
216     throws SAMLException {
217         String sessionid=null;
218         StringBuffer pproviderId = // Get back IdP Entity name from SAML
219             new StringBuffer();
220         
221         ShibBrowserProfile profile = new ShibBrowserProfile(applicationId);
222         BrowserProfileResponse samldata = profile.receive(
223                 pproviderId,
224                 req,
225                 shireURL,   // My URL (Why??) To prevent attackers from redirecting messages. 
226                 context.getReplayCache(),
227                 null,
228                 1
229         );
230         
231         Iterator conditions = samldata.assertion.getConditions();
232         while (conditions.hasNext()) {
233             SAMLCondition cond =
234                 (SAMLCondition)conditions.next();
235             
236             if (cond instanceof SAMLAudienceRestrictionCondition) {
237                 SAMLAudienceRestrictionCondition audienceCondition =
238                     (SAMLAudienceRestrictionCondition) cond;
239                 Iterator audiences = audienceCondition.getAudiences();
240                 if (audiences==null)
241                     continue; // probably invalid
242                 boolean matched = false;
243                 while (audiences.hasNext()) {
244                     String audienceString = (String) audiences.next();
245                     if (audienceString.equals(providerId)) {
246                         matched=true;
247                         break;
248                     }
249                 }
250                 if (!matched) {
251                     throw new SAMLException("Assertion restricted to other audiences.");
252                 }
253             }
254         }
255         
256         // The Authentication Assertion gets placed in a newly created
257         // Session object. Later, someone will get an Attribute Assertion
258         // and add it to the Session. The SessionID key is returned to
259         // the Browser as a Cookie.
260         SessionManager sessionManager = context.getSessionManager();
261         sessionid = sessionManager.newSession(
262                 applicationId, 
263                 ipaddr, 
264                 pproviderId.toString(), 
265                 samldata.assertion, 
266                 samldata.authnStatement,
267                 emptySessionId);
268         
269         // Very agressive attribute fetch rule 
270         // Get the Attributes immediately! [good for debugging]
271         Session session = sessionManager.findSession(sessionid, applicationId);
272         AttributeRequestor.fetchAttributes(session);
273
274         return sessionid;
275     }
276
277
278
279     protected void doGet(HttpServletRequest arg0, HttpServletResponse arg1)
280         throws ServletException, IOException {
281         // Currently the Assertion Consumer does not receive a GET
282     }
283         
284
285 }