Fix minor issues, make xsd relative for editors
[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.File;
52 import java.io.IOException;
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.opensaml.SAMLException;
63 import org.opensaml.SAMLBrowserProfile;
64 import org.opensaml.SAMLResponse;
65 import org.opensaml.SAMLBrowserProfile.BrowserProfileResponse;
66 import org.w3c.dom.Element;
67 import org.apache.log4j.FileAppender;
68 import org.apache.log4j.Layout;
69 import org.apache.log4j.Level;
70 import org.apache.log4j.Logger;
71 import org.apache.log4j.PatternLayout;
72 import org.apache.xml.security.Init;
73
74 import x0.maceShibbolethTargetConfig1.SessionsDocument.Sessions;
75
76 import edu.internet2.middleware.commons.log4j.ThreadLocalAppender;
77 import edu.internet2.middleware.shibboleth.common.Credentials;
78 import edu.internet2.middleware.shibboleth.common.ShibBrowserProfile;
79 import edu.internet2.middleware.shibboleth.metadata.MetadataException;
80 import edu.internet2.middleware.shibboleth.resource.AuthenticationFilter;
81 import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderConfig.ApplicationInfo;
82
83 /**
84  * AuthenticatonAssertionConsumerServlet
85  * 
86  * @author Howard Gilbert
87  */
88 public class AssertionConsumerServlet extends HttpServlet {
89
90         private static Logger log = null;
91         
92         private static ServiceProviderContext context = ServiceProviderContext.getInstance();
93         
94         private Element                 configuration = null;
95         private Credentials             credentials = null;
96         
97         public static final String SESSIONPARM =
98             "ShibbolethSessionId";
99         
100         
101         public void init() throws ServletException {
102                 super.init();
103                 ServletContext servletContext = this.getServletContext();
104                 
105                 Init.init();
106
107                 // Initialize logging specially
108                 Logger targetLogger = Logger.getLogger("edu.internet2.middleware");
109                 Logger samlLogger = Logger.getLogger("org.opensaml");
110                 File diagdir = new File(servletContext.getRealPath("/diagnose"));
111                 diagdir.mkdirs();
112                 String logname = servletContext.getRealPath("/diagnose/initialize.log");
113                 Layout initLayout = new PatternLayout("%d{HH:mm} %-5p %m%n");
114                 
115                 try {
116             FileAppender initLogAppender = new FileAppender(initLayout,logname);
117             ThreadLocalAppender threadAppender = new ThreadLocalAppender();
118             threadAppender.setLayout(initLayout);
119             targetLogger.setAdditivity(false);
120             targetLogger.addAppender(initLogAppender);
121             targetLogger.addAppender(threadAppender);
122             targetLogger.setLevel(Level.DEBUG);
123             samlLogger.addAppender(threadAppender);
124             samlLogger.setLevel(Level.DEBUG);
125         } catch (IOException e) {
126             e.printStackTrace();
127         }
128                 
129 /*              ConsoleAppender rootAppender = new ConsoleAppender();
130                 rootAppender.setWriter(new PrintWriter(System.out));
131                 rootAppender.setName("stdout");
132                 targetLogger.addAppender(rootAppender);
133
134                 // rootAppender.setLayout(new PatternLayout("%-5p %-41X{serviceId} %d{ISO8601} (%c:%L) - %m%n"));
135                 // Logger.getRootLogger().setLevel((Level) Level.DEBUG);
136                 Logger.getRootLogger().setLevel((Level) Level.INFO);
137                 rootAppender.setLayout(new PatternLayout("%d{ISO8601} %-5p %-41X{serviceId} - %m%n"));
138 */
139                 log = Logger.getLogger(AssertionConsumerServlet.class.getName());
140                 
141                 ServletContextInitializer.initServiceProvider(servletContext);
142                 AuthenticationFilter.setFilterSupport(new FilterSupportImpl());
143         }
144
145
146
147         /**
148          * Accept the SAML Assertion post from the HS.
149          * 
150          * @param request the request send by the client to the server
151          * @param response the response send by the server to the client
152          * @throws ServletException if an error occurred
153          * @throws IOException if an error occurred
154          */
155         public void doPost(
156                 HttpServletRequest request,
157                 HttpServletResponse response)
158                 // throws ServletException, IOException 
159                 {
160             ServletContextInitializer.beginService(request,response);
161                 try {
162             ServiceProviderConfig config = context.getServiceProviderConfig();
163             
164             String ipaddr = request.getRemoteAddr();
165             
166             // URL of Resource that triggered authorization
167             // XXX: I added support to the profile for extracting TARGET, but
168             // it's not too critical in Java since you can grab it easily anyway.
169             // Might be better in the 2.0 future though, since the bindings get trickier.
170             String target = request.getParameter("TARGET");
171             
172             // Map the Resource URL into an <Application>
173             String applicationId = config.mapRequest(target);
174             ApplicationInfo appinfo = config.getApplication(applicationId);
175             Sessions appSessionValues = appinfo.getApplicationConfig().getSessions();
176             String shireURL = request.getRequestURL().toString();
177             String providerId = appinfo.getApplicationConfig().getProviderId();
178             
179            
180             if (appSessionValues.getShireSSL()&& // Requires SSL
181                         !request.isSecure()) {       // isn't SSL
182                 log.error("Authentication Assersion not posted over SSL.");
183                 try {
184                     response.sendRedirect("/shibboleth/shireError.html");
185                 } catch (IOException e1) {
186                 }
187                 return;
188             }
189             
190             log.debug("Authentication received from "+ipaddr+" for "+target+
191                         "(application:"+applicationId+") (Provider:"+providerId+")");
192
193             String sessionId = createSessionFromPost(ipaddr, request, applicationId, shireURL, providerId, null);
194             
195             Cookie cookie = new Cookie("ShibbolethSPSession",sessionId);
196             response.addCookie(cookie);
197             
198             try {
199                                 if (target.equals("SendAttributesBackToMe")) {
200                                         ServletOutputStream outputStream = response.getOutputStream();
201                                         response.setContentType("text/xml");
202                                         Session session = context.getSessionManager().findSession(sessionId,applicationId);
203                                         SAMLResponse attributeResponse = session.getAttributeResponse();
204                                         outputStream.print(attributeResponse.toString());
205                                 } else {
206                                         response.sendRedirect(target+"?"+SESSIONPARM+"="+sessionId);
207                                 }
208             }
209             catch (IOException e) {
210             }
211         }
212         catch (MetadataException e) {
213             log.error("Authentication Assertion source not found in Metadata.");
214             try {
215                 response.sendRedirect("/shibboleth/shireError.html");
216             }
217             catch (IOException e1) {
218             }
219         }
220         catch (SAMLException e) {
221             log.error("Authentication Assertion had invalid format.");
222             try {
223                 response.sendRedirect("/shibboleth/shireError.html");
224             }
225             catch (IOException e1) {
226             }
227         }
228         finally {
229             ServletContextInitializer.finishService(request,response);
230         }
231         }
232         
233     /**
234      * Create a Session object from SHIRE POST data
235      * 
236      * @param ipaddr IP Address of Browser
237      * @param bin64Assertion Authentication assertion from POST
238      * @param applicationId from RequestMap
239      * @param shireURL 
240      * @param providerId Our Entity name
241      * @return random key of Session
242      * @throws SAMLException
243      */
244     public static 
245     String createSessionFromPost(
246             String ipaddr, 
247             HttpServletRequest req, 
248             String applicationId, 
249             String shireURL, 
250             String providerId,
251             String emptySessionId
252             ) 
253     throws SAMLException {
254         String sessionid=null;
255         StringBuffer pproviderId = // Get back IdP Entity name from SAML
256             new StringBuffer();
257         String[] audiences = new String[1];
258         audiences[0]=providerId;
259         
260         ShibBrowserProfile profile = new ShibBrowserProfile(applicationId);
261         BrowserProfileResponse samldata = profile.receive(
262                 pproviderId,
263                 req,
264                 shireURL,   // My URL (Why??) To prevent attackers from redirecting messages. 
265                 SAMLBrowserProfile.PROFILE_POST,    // TODO: support both profiles 
266                 context.getReplayCache(),
267                 null,
268                 1
269         );
270         
271         // TODO: Audience/condition checking is now the profile caller's job.
272         
273         // The Authentication Assertion gets placed in a newly created
274         // Session object. Later, someone will get an Attribute Assertion
275         // and add it to the Session. The SessionID key is returned to
276         // the Browser as a Cookie.
277         SessionManager sessionManager = context.getSessionManager();
278         sessionid = sessionManager.newSession(
279                 applicationId, 
280                 ipaddr, 
281                 pproviderId.toString(), 
282                 samldata.assertion, 
283                 samldata.authnStatement,
284                 emptySessionId);
285         
286         // Very agressive attribute fetch rule 
287         // Get the Attributes immediately! [good for debugging]
288         Session session = sessionManager.findSession(sessionid, applicationId);
289         AttributeRequestor.fetchAttributes(session);
290
291         return sessionid;
292     }
293
294
295
296     protected void doGet(HttpServletRequest arg0, HttpServletResponse arg1)
297         throws ServletException, IOException {
298         // TODO Auto-generated method stub
299         super.doGet(arg0, arg1);
300     }
301         
302
303 }