Process Authentication POST
[java-idp.git] / src / edu / internet2 / middleware / shibboleth / target / 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.target;
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.target.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                 String logname = servletContext.getRealPath("/diagnose/initialize.log");
101                 Layout initLayout = new PatternLayout("%d{HH:mm} %-5p %m%n");
102                 
103                 try {
104             FileAppender initLogAppender = new FileAppender(initLayout,logname);
105             ThreadLocalAppender threadAppender = new ThreadLocalAppender();
106             threadAppender.setLayout(initLayout);
107             targetLogger.setAdditivity(false);
108             targetLogger.addAppender(initLogAppender);
109             targetLogger.addAppender(threadAppender);
110             targetLogger.setLevel(Level.DEBUG);
111         } catch (IOException e) {
112             e.printStackTrace();
113         }
114                 
115 /*              ConsoleAppender rootAppender = new ConsoleAppender();
116                 rootAppender.setWriter(new PrintWriter(System.out));
117                 rootAppender.setName("stdout");
118                 targetLogger.addAppender(rootAppender);
119
120                 // rootAppender.setLayout(new PatternLayout("%-5p %-41X{serviceId} %d{ISO8601} (%c:%L) - %m%n"));
121                 // Logger.getRootLogger().setLevel((Level) Level.DEBUG);
122                 Logger.getRootLogger().setLevel((Level) Level.INFO);
123                 rootAppender.setLayout(new PatternLayout("%d{ISO8601} %-5p %-41X{serviceId} - %m%n"));
124 */
125                 log = Logger.getLogger(AuthenticationAssertionConsumerServlet.class.getName());
126                 
127                 ServletContextInitializer.initServiceProvider(servletContext);
128                 AuthenticationFilter.setFilterSupport(new FilterSupportImpl());
129                 
130         }
131
132
133
134         /**
135          * Accept the SAML Assertion post from the HS.
136          * 
137          * @param request the request send by the client to the server
138          * @param response the response send by the server to the client
139          * @throws ServletException if an error occurred
140          * @throws IOException if an error occurred
141          */
142         public void doPost(
143                 HttpServletRequest request,
144                 HttpServletResponse response)
145                 // throws ServletException, IOException 
146                 {
147             ServletContextInitializer.beginService(request,response);
148                 try {
149             ServiceProviderConfig config = context.getServiceProviderConfig();
150             
151             String ipaddr = request.getRemoteAddr();
152             
153             // URL of Resource that triggered authorization
154             String target = request.getParameter("TARGET");
155             
156             // Bin64 encoded SAML Authentication Assertion from HS
157             String assertparm = request.getParameter("SAMLResponse");
158             byte [] bin64Assertion = assertparm.getBytes();
159             
160             // Map the Resource URL into an <Application>
161             String applicationId = config.mapRequest(target);
162             ApplicationInfo appinfo = config.getApplication(applicationId);
163             Sessions appSessionValues = appinfo.getApplicationConfig().getSessions();
164             
165             // Sanity check:
166             // I am the SHIRE. So the SHIRE URL should be the one in the 
167             // HttpRequest. However, it might have been stepped on by a filter
168             // or frontend. This is the configured cannonical URL that was 
169             // passed to the filter, sent to the HS, and used by the browser
170             // in the redirect. If I need (for whatever reason) to pass a 
171             // Shire URL to the POST processing, lets use the configured one
172             String shireURL = appSessionValues.getShireURL();
173             
174             // Provider ID of me, the Service Provider, for this application
175             String providerId = appinfo.getApplicationConfig().getProviderId();
176             String[] audiences = new String[1];
177             audiences[0]=providerId;
178             
179             if (appSessionValues.getShireSSL()&& // Requires SSL
180                         !request.isSecure()) {       // isn't SSL
181                 log.error("Authentication Assersion not posted over SSL.");
182                 response.sendRedirect("/shireError.html");
183             }
184             
185             log.debug("Authentication received from "+ipaddr+" for "+target+
186                         "(application:"+applicationId+") (Provider:"+providerId+")");
187             
188             // Unfortunately, the previous mix of Java and C had about 100 things
189             // called "providers". In this particular case, we are passing to 
190             // the POST processing layer an empty StringBuffer into which will be
191             // placed a second return value (tricky!). This will be the ID of the
192             // Origin. 
193             StringBuffer pproviderId = new StringBuffer();
194             
195             SAMLResponse samldata = null;       
196             SAMLAssertion assertion = null;
197             SAMLAuthenticationStatement authstmt = null;
198             try { 
199                 ShibPOSTProfile profile = new ShibPOSTProfile(applicationId);
200                 samldata = profile.accept(
201                         bin64Assertion, // Assertion from POST of Form field
202                         shireURL,   // My URL (Why??)
203                         60, 
204                         audiences,  // My "Provider" (Entity) ID
205                         pproviderId // HS "Provider" (Entity) ID returned
206                         );
207                 
208                 assertion = SAMLPOSTProfile.getSSOAssertion(samldata,
209                         Collections.singleton(providerId));
210                 authstmt = SAMLPOSTProfile.getSSOStatement(assertion);
211                 
212             } catch (SAMLException e) {
213                 log.error("Authentication Assertion had invalid format.");
214                 response.sendRedirect("/shireError.html");
215                 return;
216             }
217             catch (MetadataException e) {
218                 log.error("Authentication Assertion source not found in Metadata.");
219                 response.sendRedirect("/shireError.html");
220                 return;
221             }
222
223             
224             // The Authentication Assertion gets placed in a newly created
225             // Session object. Later, someone will get an Attribute Assertion
226             // and add it to the Session. The SessionID key is returned to
227             // the Browser as a Cookie.
228             SessionManager sessionManager = context.getSessionManager();
229             String sessionid = sessionManager.newSession(
230                     applicationId, ipaddr, pproviderId.toString(), assertion, authstmt);
231             Cookie cookie = new Cookie("ShibbolethSPSession",sessionid);
232             response.addCookie(cookie);
233             
234             // Very agressive attribute fetch rule 
235             // Get the Attributes immediately! [good for debugging]
236             Session session = sessionManager.findSession(sessionid, applicationId);
237             boolean gotattributes = AttributeRequestor.fetchAttributes(session);
238             if (!gotattributes)
239                 response.sendRedirect("/shireError.html");
240             
241             log.debug(SessionManager.dumpAttributes(session));
242             
243             response.sendRedirect(target+"?"+SESSIONPARM+"="+sessionid);
244         } catch (IOException e) {
245             // A sendRedirect() failed. 
246             // This can only happen if the user closed the Browser.
247             // Nothing to do
248         } finally {
249             ServletContextInitializer.finishService(request,response);
250         }
251
252         }
253         
254     protected void doGet(HttpServletRequest arg0, HttpServletResponse arg1)
255         throws ServletException, IOException {
256         // TODO Auto-generated method stub
257         super.doGet(arg0, arg1);
258     }
259         
260
261 }