Support reserving SessionId before WAYF Redirect
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / serviceprovider / AuthenticationAssertionConsumerServlet.java
1 /*
2  * AuthenticatonAssertionConsumerServlet
3  * 
4  * The Shibboleth function previous known as the SHIRE.
5  * 
6  * Authentication Assertion Consumer is the SAML 2.0 term for what the
7  * SHIRE does. A SAML Assertion containing an Authentication statement
8  * with the "principal" identifier value equal to the handle vended by
9  * the Handle Server is received from the Browser. The Handle Server
10  * generated a form, prefilled it with a Bin64 encoding of the SAML
11  * statement, and included Javascript to automatically submit the form
12  * to this URL.
13  * 
14  * All HTTP, HTML, and servlet logic is localized to this layer of
15  * modules. Any information must be extracted from the Servlet API
16  * to be passed directly to Shibboleth.
17  * 
18  * The work is done by a ShibPOSTProfile object. It takes the Bin64
19  * encoded string, converts it to a SAMLObject, verifies structure,
20  * and validates signatures.
21  * 
22  * The resulting Authentication Assertion SAML statement is passed
23  * to Session Manager to create a new session. This process feeds
24  * back a session identifier that becomes the value of a Cookie sent
25  * back to the Browser to track the session.
26  * 
27  * If the decision is made to fetch attributes immediately, the 
28  * Session object can be passed to the static AttributeRequestor
29  * service. It generates a ShibBinding, sends a request to the AA,
30  * validates the response, applies AAP, and stores the resulting 
31  * SAML Attribute Assertion in the Session object.
32  * 
33  * --------------------
34  * Copyright 2002, 2004 
35  * University Corporation for Advanced Internet Development, Inc. 
36  * All rights reserved
37  * [Thats all we have to say to protect ourselves]
38  * Your permission to use this code is governed by "The Shibboleth License".
39  * A copy may be found at http://shibboleth.internet2.edu/license.html
40  * [Nothing in copyright law requires license text in every file.]
41  */
42 package edu.internet2.middleware.shibboleth.serviceprovider;
43
44 import java.io.IOException;
45 import java.util.Collections;
46
47 import javax.servlet.ServletContext;
48 import javax.servlet.ServletException;
49 import javax.servlet.http.Cookie;
50 import javax.servlet.http.HttpServlet;
51 import javax.servlet.http.HttpServletRequest;
52 import javax.servlet.http.HttpServletResponse;
53 import org.opensaml.SAMLAssertion;
54 import org.opensaml.SAMLAuthenticationStatement;
55 import org.opensaml.SAMLException;
56 import org.opensaml.SAMLPOSTProfile;
57 import org.opensaml.SAMLResponse;
58 import org.w3c.dom.Element;
59 import org.apache.log4j.FileAppender;
60 import org.apache.log4j.Layout;
61 import org.apache.log4j.Level;
62 import org.apache.log4j.Logger;
63 import org.apache.log4j.PatternLayout;
64 import org.apache.xml.security.Init;
65
66 import x0.maceShibbolethTargetConfig1.SessionsDocument.Sessions;
67
68 import edu.internet2.middleware.commons.log4j.ThreadLocalAppender;
69 import edu.internet2.middleware.shibboleth.common.Credentials;
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  * AuthenticatonAssertionConsumerServlet
76  * 
77  * @author Howard Gilbert
78  */
79 public class AuthenticationAssertionConsumerServlet extends HttpServlet {
80
81         private static Logger log = null;
82         
83         private static ServiceProviderContext context = ServiceProviderContext.getInstance();
84         
85         private Element                 configuration;
86         private Credentials                             credentials;
87         
88         public static final String SESSIONPARM =
89             "ShibbolethSessionId";
90         
91         
92         public void init() throws ServletException {
93                 super.init();
94                 ServletContext servletContext = this.getServletContext();
95                 
96                 Init.init();
97
98                 // Initialize logging specially
99                 Logger targetLogger = Logger.getLogger("edu.internet2.middleware");
100                 Logger samlLogger = Logger.getLogger("org.opensaml");
101                 String logname = servletContext.getRealPath("/diagnose/initialize.log");
102                 Layout initLayout = new PatternLayout("%d{HH:mm} %-5p %m%n");
103                 
104                 try {
105             FileAppender initLogAppender = new FileAppender(initLayout,logname);
106             ThreadLocalAppender threadAppender = new ThreadLocalAppender();
107             threadAppender.setLayout(initLayout);
108             targetLogger.setAdditivity(false);
109             targetLogger.addAppender(initLogAppender);
110             targetLogger.addAppender(threadAppender);
111             targetLogger.setLevel(Level.DEBUG);
112             samlLogger.addAppender(threadAppender);
113             samlLogger.setLevel(Level.DEBUG);
114         } catch (IOException e) {
115             e.printStackTrace();
116         }
117                 
118 /*              ConsoleAppender rootAppender = new ConsoleAppender();
119                 rootAppender.setWriter(new PrintWriter(System.out));
120                 rootAppender.setName("stdout");
121                 targetLogger.addAppender(rootAppender);
122
123                 // rootAppender.setLayout(new PatternLayout("%-5p %-41X{serviceId} %d{ISO8601} (%c:%L) - %m%n"));
124                 // Logger.getRootLogger().setLevel((Level) Level.DEBUG);
125                 Logger.getRootLogger().setLevel((Level) Level.INFO);
126                 rootAppender.setLayout(new PatternLayout("%d{ISO8601} %-5p %-41X{serviceId} - %m%n"));
127 */
128                 log = Logger.getLogger(AuthenticationAssertionConsumerServlet.class.getName());
129                 
130                 ServletContextInitializer.initServiceProvider(servletContext);
131                 AuthenticationFilter.setFilterSupport(new FilterSupportImpl());
132                 
133         }
134
135
136
137         /**
138          * Accept the SAML Assertion post from the HS.
139          * 
140          * @param request the request send by the client to the server
141          * @param response the response send by the server to the client
142          * @throws ServletException if an error occurred
143          * @throws IOException if an error occurred
144          */
145         public void doPost(
146                 HttpServletRequest request,
147                 HttpServletResponse response)
148                 // throws ServletException, IOException 
149                 {
150             ServletContextInitializer.beginService(request,response);
151                 try {
152             ServiceProviderConfig config = context.getServiceProviderConfig();
153             
154             String ipaddr = request.getRemoteAddr();
155             
156             // URL of Resource that triggered authorization
157             String target = request.getParameter("TARGET");
158             
159             // Bin64 encoded SAML Authentication Assertion from HS
160             String assertparm = request.getParameter("SAMLResponse");
161             byte [] bin64Assertion = assertparm.getBytes();
162             
163             // Map the Resource URL into an <Application>
164             String applicationId = config.mapRequest(target);
165             ApplicationInfo appinfo = config.getApplication(applicationId);
166             Sessions appSessionValues = appinfo.getApplicationConfig().getSessions();
167             String shireURL = appSessionValues.getShireURL();
168             String providerId = appinfo.getApplicationConfig().getProviderId();
169             
170            
171             if (appSessionValues.getShireSSL()&& // Requires SSL
172                         !request.isSecure()) {       // isn't SSL
173                 log.error("Authentication Assersion not posted over SSL.");
174                 try {
175                     response.sendRedirect("/shibboleth/shireError.html");
176                 } catch (IOException e1) {
177                 }
178                 return;
179             }
180             
181             log.debug("Authentication received from "+ipaddr+" for "+target+
182                         "(application:"+applicationId+") (Provider:"+providerId+")");
183
184             String sessionId = createSessionFromPost(ipaddr, bin64Assertion, applicationId, shireURL, providerId, null);
185             
186             Cookie cookie = new Cookie("ShibbolethSPSession",sessionId);
187             response.addCookie(cookie);
188             
189             try {
190                 response.sendRedirect(target+"?"+SESSIONPARM+"="+sessionId);
191             } catch (IOException e) {}
192         } catch (SAMLException e) {
193                 log.error("Authentication Assertion had invalid format.");
194                 try {
195                 response.sendRedirect("/shibboleth/shireError.html");
196             } catch (IOException e1) {}
197         }
198         catch (MetadataException e) {
199                 log.error("Authentication Assertion source not found in Metadata.");
200                 try {
201                 response.sendRedirect("/shibboleth/shireError.html");
202             } catch (IOException e1) {}
203         } finally {
204             ServletContextInitializer.finishService(request,response);
205         }
206
207         }
208         
209     /**
210      * Create a Session object from SHIRE POST data
211      * 
212      * @param ipaddr IP Address of Browser
213      * @param bin64Assertion Authentication assertion from POST
214      * @param applicationId from RequestMap
215      * @param shireURL 
216      * @param providerId Our Entity name
217      * @return random key of Session
218      * @throws SAMLException
219      * @throws MetadataException
220      */
221     public static 
222     String createSessionFromPost(
223             String ipaddr, 
224             byte[] bin64Assertion, 
225             String applicationId, 
226             String shireURL, 
227             String providerId,
228             String emptySessionId
229             ) 
230     throws SAMLException, MetadataException {
231         String sessionid=null;
232         StringBuffer pproviderId = // Get back Origin Entity name from SAML
233             new StringBuffer();
234         String[] audiences = new String[1];
235         audiences[0]=providerId;
236         
237         SAMLResponse samldata = null;   
238         SAMLAssertion assertion = null;
239         SAMLAuthenticationStatement authstmt = null;
240         ShibPOSTProfile profile = new ShibPOSTProfile(applicationId);
241         samldata = profile.accept(
242                 bin64Assertion, // Assertion from POST of Form field
243                 shireURL,   // My URL (Why??)
244                 60, 
245                 audiences,  // My "Provider" (Entity) ID
246                 pproviderId // HS "Provider" (Entity) ID returned
247         );
248         
249         assertion = SAMLPOSTProfile.getSSOAssertion(samldata,
250                 Collections.singleton(providerId));
251         authstmt = SAMLPOSTProfile.getSSOStatement(assertion);
252         
253
254         
255         // The Authentication Assertion gets placed in a newly created
256         // Session object. Later, someone will get an Attribute Assertion
257         // and add it to the Session. The SessionID key is returned to
258         // the Browser as a Cookie.
259         SessionManager sessionManager = context.getSessionManager();
260         sessionid = sessionManager.newSession(
261                 applicationId, 
262                 ipaddr, 
263                 pproviderId.toString(), 
264                 assertion, 
265                 authstmt,
266                 emptySessionId);
267         
268         // Very agressive attribute fetch rule 
269         // Get the Attributes immediately! [good for debugging]
270         Session session = sessionManager.findSession(sessionid, applicationId);
271         AttributeRequestor.fetchAttributes(session);
272
273         return sessionid;
274     }
275
276
277
278     protected void doGet(HttpServletRequest arg0, HttpServletResponse arg1)
279         throws ServletException, IOException {
280         // TODO Auto-generated method stub
281         super.doGet(arg0, arg1);
282     }
283         
284
285 }