2 * AuthenticatonAssertionConsumerServlet
4 * The Shibboleth function previous known as the SHIRE.
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
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.
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.
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.
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.
33 * --------------------
34 * Copyright 2002, 2004
35 * University Corporation for Advanced Internet Development, Inc.
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.]
42 package edu.internet2.middleware.shibboleth.serviceprovider;
45 import java.io.IOException;
47 import javax.servlet.ServletContext;
48 import javax.servlet.ServletException;
49 import javax.servlet.ServletOutputStream;
50 import javax.servlet.http.Cookie;
51 import javax.servlet.http.HttpServlet;
52 import javax.servlet.http.HttpServletRequest;
53 import javax.servlet.http.HttpServletResponse;
55 import org.opensaml.SAMLException;
56 import org.opensaml.SAMLBrowserProfile;
57 import org.opensaml.SAMLResponse;
58 import org.opensaml.SAMLBrowserProfile.BrowserProfileResponse;
59 import org.w3c.dom.Element;
60 import org.apache.log4j.FileAppender;
61 import org.apache.log4j.Layout;
62 import org.apache.log4j.Level;
63 import org.apache.log4j.Logger;
64 import org.apache.log4j.PatternLayout;
65 import org.apache.xml.security.Init;
67 import x0.maceShibbolethTargetConfig1.SessionsDocument.Sessions;
69 import edu.internet2.middleware.commons.log4j.ThreadLocalAppender;
70 import edu.internet2.middleware.shibboleth.common.Credentials;
71 import edu.internet2.middleware.shibboleth.common.ShibBrowserProfile;
72 import edu.internet2.middleware.shibboleth.metadata.MetadataException;
73 import edu.internet2.middleware.shibboleth.resource.AuthenticationFilter;
74 import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderConfig.ApplicationInfo;
77 * AuthenticatonAssertionConsumerServlet
79 * @author Howard Gilbert
81 public class AuthenticationAssertionConsumerServlet extends HttpServlet {
83 private static Logger log = null;
85 private static ServiceProviderContext context = ServiceProviderContext.getInstance();
87 private Element configuration = null;
88 private Credentials credentials = null;
90 public static final String SESSIONPARM =
91 "ShibbolethSessionId";
94 public void init() throws ServletException {
96 ServletContext servletContext = this.getServletContext();
100 // Initialize logging specially
101 Logger targetLogger = Logger.getLogger("edu.internet2.middleware");
102 Logger samlLogger = Logger.getLogger("org.opensaml");
103 File diagdir = new File(servletContext.getRealPath("/diagnose"));
105 String logname = servletContext.getRealPath("/diagnose/initialize.log");
106 Layout initLayout = new PatternLayout("%d{HH:mm} %-5p %m%n");
109 FileAppender initLogAppender = new FileAppender(initLayout,logname);
110 ThreadLocalAppender threadAppender = new ThreadLocalAppender();
111 threadAppender.setLayout(initLayout);
112 targetLogger.setAdditivity(false);
113 targetLogger.addAppender(initLogAppender);
114 targetLogger.addAppender(threadAppender);
115 targetLogger.setLevel(Level.DEBUG);
116 samlLogger.addAppender(threadAppender);
117 samlLogger.setLevel(Level.DEBUG);
118 } catch (IOException e) {
122 /* ConsoleAppender rootAppender = new ConsoleAppender();
123 rootAppender.setWriter(new PrintWriter(System.out));
124 rootAppender.setName("stdout");
125 targetLogger.addAppender(rootAppender);
127 // rootAppender.setLayout(new PatternLayout("%-5p %-41X{serviceId} %d{ISO8601} (%c:%L) - %m%n"));
128 // Logger.getRootLogger().setLevel((Level) Level.DEBUG);
129 Logger.getRootLogger().setLevel((Level) Level.INFO);
130 rootAppender.setLayout(new PatternLayout("%d{ISO8601} %-5p %-41X{serviceId} - %m%n"));
132 log = Logger.getLogger(AuthenticationAssertionConsumerServlet.class.getName());
134 ServletContextInitializer.initServiceProvider(servletContext);
135 AuthenticationFilter.setFilterSupport(new FilterSupportImpl());
141 * Accept the SAML Assertion post from the HS.
143 * @param request the request send by the client to the server
144 * @param response the response send by the server to the client
145 * @throws ServletException if an error occurred
146 * @throws IOException if an error occurred
149 HttpServletRequest request,
150 HttpServletResponse response)
151 // throws ServletException, IOException
153 ServletContextInitializer.beginService(request,response);
155 ServiceProviderConfig config = context.getServiceProviderConfig();
157 String ipaddr = request.getRemoteAddr();
159 // URL of Resource that triggered authorization
160 // XXX: I added support to the profile for extracting TARGET, but
161 // it's not too critical in Java since you can grab it easily anyway.
162 // Might be better in the 2.0 future though, since the bindings get trickier.
163 String target = request.getParameter("TARGET");
165 // Map the Resource URL into an <Application>
166 String applicationId = config.mapRequest(target);
167 ApplicationInfo appinfo = config.getApplication(applicationId);
168 Sessions appSessionValues = appinfo.getApplicationConfig().getSessions();
169 String shireURL = appSessionValues.getShireURL();
170 String providerId = appinfo.getApplicationConfig().getProviderId();
173 if (appSessionValues.getShireSSL()&& // Requires SSL
174 !request.isSecure()) { // isn't SSL
175 log.error("Authentication Assersion not posted over SSL.");
177 response.sendRedirect("/shibboleth/shireError.html");
178 } catch (IOException e1) {
183 log.debug("Authentication received from "+ipaddr+" for "+target+
184 "(application:"+applicationId+") (Provider:"+providerId+")");
186 String sessionId = createSessionFromPost(ipaddr, request, applicationId, shireURL, providerId, null);
188 Cookie cookie = new Cookie("ShibbolethSPSession",sessionId);
189 response.addCookie(cookie);
192 if (target.equals("SendAttributesBackToMe")) {
193 ServletOutputStream outputStream = response.getOutputStream();
194 response.setContentType("text/xml");
195 Session session = context.getSessionManager().findSession(sessionId,applicationId);
196 SAMLResponse attributeResponse = session.getAttributeResponse();
197 outputStream.print(attributeResponse.toString());
199 response.sendRedirect(target+"?"+SESSIONPARM+"="+sessionId);
202 catch (IOException e) {
205 catch (MetadataException e) {
206 log.error("Authentication Assertion source not found in Metadata.");
208 response.sendRedirect("/shibboleth/shireError.html");
210 catch (IOException e1) {
213 catch (SAMLException e) {
214 log.error("Authentication Assertion had invalid format.");
216 response.sendRedirect("/shibboleth/shireError.html");
218 catch (IOException e1) {
222 ServletContextInitializer.finishService(request,response);
227 * Create a Session object from SHIRE POST data
229 * @param ipaddr IP Address of Browser
230 * @param bin64Assertion Authentication assertion from POST
231 * @param applicationId from RequestMap
233 * @param providerId Our Entity name
234 * @return random key of Session
235 * @throws SAMLException
238 String createSessionFromPost(
240 HttpServletRequest req,
241 String applicationId,
244 String emptySessionId
246 throws SAMLException {
247 String sessionid=null;
248 StringBuffer pproviderId = // Get back IdP Entity name from SAML
250 String[] audiences = new String[1];
251 audiences[0]=providerId;
253 ShibBrowserProfile profile = new ShibBrowserProfile(applicationId);
254 BrowserProfileResponse samldata = profile.receive(
257 shireURL, // My URL (Why??) To prevent attackers from redirecting messages.
258 SAMLBrowserProfile.PROFILE_POST, // TODO: support both profiles
259 context.getReplayCache(),
264 // TODO: Audience/condition checking is now the profile caller's job.
266 // The Authentication Assertion gets placed in a newly created
267 // Session object. Later, someone will get an Attribute Assertion
268 // and add it to the Session. The SessionID key is returned to
269 // the Browser as a Cookie.
270 SessionManager sessionManager = context.getSessionManager();
271 sessionid = sessionManager.newSession(
274 pproviderId.toString(),
276 samldata.authnStatement,
279 // Very agressive attribute fetch rule
280 // Get the Attributes immediately! [good for debugging]
281 Session session = sessionManager.findSession(sessionid, applicationId);
282 AttributeRequestor.fetchAttributes(session);
289 protected void doGet(HttpServletRequest arg0, HttpServletResponse arg1)
290 throws ServletException, IOException {
291 // TODO Auto-generated method stub
292 super.doGet(arg0, arg1);