change java.net.URI argument to opensaml URI type (wrapped string)
[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.SAMLBrowserProfile;
65 import org.opensaml.SAMLCondition;
66 import org.opensaml.SAMLException;
67 import org.opensaml.SAMLResponse;
68 import org.opensaml.SAMLBrowserProfile.BrowserProfileResponse;
69 import x0.maceShibbolethTargetConfig1.SessionsDocument.Sessions;
70 import edu.internet2.middleware.shibboleth.common.ShibBrowserProfile;
71 import edu.internet2.middleware.shibboleth.metadata.MetadataException;
72 import edu.internet2.middleware.shibboleth.resource.AuthenticationFilter;
73 import edu.internet2.middleware.shibboleth.serviceprovider.ServiceProviderConfig.ApplicationInfo;
74
75 /**
76  * AuthenticatonAssertionConsumerServlet
77  * 
78  * @author Howard Gilbert
79  */
80 public class AssertionConsumerServlet extends HttpServlet {
81
82         private static Logger log = Logger.getLogger(AssertionConsumerServlet.class.getName());
83         
84         private static ServiceProviderContext context = ServiceProviderContext.getInstance();
85         
86         public static final String SESSIONPARM =
87             "ShibbolethSessionId";
88         
89         
90         public void init() throws ServletException {
91                 super.init();
92                 ServletContext servletContext = this.getServletContext();
93                 
94                 // Note: the ServletContext should have been initialized by the Listener
95                 ServletContextInitializer.initServiceProvider(servletContext);
96                 
97                 // Establish linkage between the SP context and the RM Filter class
98                 AuthenticationFilter.setFilterSupport(new FilterSupportImpl());
99         }
100
101
102
103         /**
104          * Accept the SAML Assertion post from the HS.
105          * 
106          * @param request the request send by the client to the server
107          * @param response the response send by the server to the client
108          * @throws ServletException if an error occurred
109          * @throws IOException if an error occurred
110          */
111         public void doPost(
112                 HttpServletRequest request,
113                 HttpServletResponse response)
114                 // throws ServletException, IOException 
115                 {
116             ServletContextInitializer.beginService(request,response);
117                 try {
118             ServiceProviderConfig config = context.getServiceProviderConfig();
119             
120             String ipaddr = request.getRemoteAddr();
121             
122             // URL of Resource that triggered authorization
123             // I added support to the profile for extracting TARGET, but
124             // it's not too critical in Java since you can grab it easily anyway.
125             // Might be better in the 2.0 future though, since the bindings get trickier.
126             String target = request.getParameter("TARGET");
127             
128             // Map the Resource URL into an <Application>
129             String applicationId = config.mapRequest(target);
130             ApplicationInfo appinfo = config.getApplication(applicationId);
131             Sessions appSessionValues = appinfo.getApplicationConfig().getSessions();
132             String shireURL = request.getRequestURL().toString();
133             String providerId = appinfo.getApplicationConfig().getProviderId();
134             
135            
136             if (appSessionValues.getShireSSL()&& // Requires SSL
137                         !request.isSecure()) {       // isn't SSL
138                 log.error("Authentication Assersion not posted over SSL.");
139                 try {
140                     response.sendRedirect("/shibboleth/shireError.html");
141                 } catch (IOException e1) {
142                 }
143                 return;
144             }
145             
146             log.debug("Authentication received from "+ipaddr+" for "+target+
147                         "(application:"+applicationId+") (Provider:"+providerId+")");
148
149             String sessionId = createSessionFromPost(ipaddr, request, applicationId, shireURL, providerId, null);
150             
151             Cookie cookie = new Cookie("ShibbolethSPSession",sessionId);
152             response.addCookie(cookie);
153             
154             try {
155                                 if (target.equals("SendAttributesBackToMe")) {
156                                         ServletOutputStream outputStream = response.getOutputStream();
157                                         response.setContentType("text/xml");
158                                         Session session = context.getSessionManager().findSession(sessionId,applicationId);
159                                         SAMLResponse attributeResponse = session.getAttributeResponse();
160                                         outputStream.print(attributeResponse.toString());
161                                 } else {
162                                         response.sendRedirect(target+"?"+SESSIONPARM+"="+sessionId);
163                                 }
164             }
165             catch (IOException e) {
166             }
167         }
168         catch (MetadataException e) {
169             log.error("Authentication Assertion source not found in Metadata.");
170             try {
171                 response.sendRedirect("/shibboleth/shireError.html");
172             }
173             catch (IOException e1) {
174             }
175         }
176         catch (SAMLException e) {
177             log.error("Authentication Assertion had invalid format.");
178             try {
179                 response.sendRedirect("/shibboleth/shireError.html");
180             }
181             catch (IOException e1) {
182             }
183         }
184         finally {
185             ServletContextInitializer.finishService(request,response);
186         }
187         }
188         
189     /**
190      * Create a Session object from SHIRE POST data
191      * 
192      * @param ipaddr IP Address of Browser
193      * @param bin64Assertion Authentication assertion from POST
194      * @param applicationId from RequestMap
195      * @param shireURL 
196      * @param providerId Our Entity name
197      * @return random key of Session
198      * @throws SAMLException
199      */
200     public static 
201     String createSessionFromPost(
202             String ipaddr, 
203             HttpServletRequest req, 
204             String applicationId, 
205             String shireURL, 
206             String providerId,
207             String emptySessionId
208             ) 
209     throws SAMLException {
210         String sessionid=null;
211         StringBuffer pproviderId = // Get back IdP Entity name from SAML
212             new StringBuffer();
213         
214         ShibBrowserProfile profile = new ShibBrowserProfile(applicationId);
215         BrowserProfileResponse samldata = profile.receive(
216                 pproviderId,
217                 req,
218                 shireURL,   // My URL (Why??) To prevent attackers from redirecting messages. 
219                 SAMLBrowserProfile.PROFILE_POST,    // TODO: support both profiles 
220                 context.getReplayCache(),
221                 null,
222                 1
223         );
224         
225         Iterator conditions = samldata.assertion.getConditions();
226         while (conditions.hasNext()) {
227             SAMLCondition cond =
228                 (SAMLCondition)conditions.next();
229             
230             if (cond instanceof SAMLAudienceRestrictionCondition) {
231                 SAMLAudienceRestrictionCondition audienceCondition =
232                     (SAMLAudienceRestrictionCondition) cond;
233                 Iterator audiences = audienceCondition.getAudiences();
234                 if (audiences==null)
235                     continue; // probably invalid
236                 boolean matched = false;
237                 while (audiences.hasNext()) {
238                     String audienceString = (String) audiences.next();
239                     if (audienceString.equals(providerId)) {
240                         matched=true;
241                         break;
242                     }
243                 }
244                 if (!matched) {
245                     throw new SAMLException("Assertion restricted to other audiences.");
246                 }
247             }
248         }
249         
250         // The Authentication Assertion gets placed in a newly created
251         // Session object. Later, someone will get an Attribute Assertion
252         // and add it to the Session. The SessionID key is returned to
253         // the Browser as a Cookie.
254         SessionManager sessionManager = context.getSessionManager();
255         sessionid = sessionManager.newSession(
256                 applicationId, 
257                 ipaddr, 
258                 pproviderId.toString(), 
259                 samldata.assertion, 
260                 samldata.authnStatement,
261                 emptySessionId);
262         
263         // Very agressive attribute fetch rule 
264         // Get the Attributes immediately! [good for debugging]
265         Session session = sessionManager.findSession(sessionid, applicationId);
266         AttributeRequestor.fetchAttributes(session);
267
268         return sessionid;
269     }
270
271
272
273     protected void doGet(HttpServletRequest arg0, HttpServletResponse arg1)
274         throws ServletException, IOException {
275         // Currently the Assertion Consumer does not receive a GET
276     }
277         
278
279 }