View Javadoc

1   // ========================================================================
2   // Copyright (c) 2008-2009 Mort Bay Consulting Pty. Ltd.
3   // ------------------------------------------------------------------------
4   // All rights reserved. This program and the accompanying materials
5   // are made available under the terms of the Eclipse Public License v1.0
6   // and Apache License v2.0 which accompanies this distribution.
7   // The Eclipse Public License is available at 
8   // http://www.eclipse.org/legal/epl-v10.html
9   // The Apache License v2.0 is available at
10  // http://www.opensource.org/licenses/apache2.0.php
11  // You may elect to redistribute this code under either of these licenses. 
12  // ========================================================================
13  
14  package org.eclipse.jetty.security.authentication;
15  
16  import java.io.IOException;
17  
18  import javax.servlet.RequestDispatcher;
19  import javax.servlet.ServletException;
20  import javax.servlet.ServletRequest;
21  import javax.servlet.ServletResponse;
22  import javax.servlet.http.HttpServletRequest;
23  import javax.servlet.http.HttpServletResponse;
24  import javax.servlet.http.HttpSession;
25  
26  import org.eclipse.jetty.http.HttpHeaders;
27  import org.eclipse.jetty.http.security.Constraint;
28  import org.eclipse.jetty.security.Authentication;
29  import org.eclipse.jetty.security.DefaultAuthentication;
30  import org.eclipse.jetty.security.ServerAuthException;
31  import org.eclipse.jetty.server.UserIdentity;
32  import org.eclipse.jetty.util.StringUtil;
33  import org.eclipse.jetty.util.URIUtil;
34  import org.eclipse.jetty.util.log.Log;
35  
36  /**
37   * @version $Rev: 4793 $ $Date: 2009-03-19 00:00:01 +0100 (Thu, 19 Mar 2009) $
38   */
39  public class FormAuthenticator extends LoginAuthenticator
40  {
41      public final static String __FORM_LOGIN_PAGE="org.eclipse.jetty.security.form_login_page";
42      public final static String __FORM_ERROR_PAGE="org.eclipse.jetty.security.form_error_page";
43      public final static String __FORM_DISPATCH="org.eclipse.jetty.security.dispatch";
44      public final static String __J_URI = "org.eclipse.jetty.util.URI";
45      public final static String __J_AUTHENTICATED = "org.eclipse.jetty.server.Auth";
46      public final static String __J_SECURITY_CHECK = "/j_security_check";
47      public final static String __J_USERNAME = "j_username";
48      public final static String __J_PASSWORD = "j_password";
49      private String _formErrorPage;
50      private String _formErrorPath;
51      private String _formLoginPage;
52      private String _formLoginPath;
53      private boolean _dispatch;
54  
55      public FormAuthenticator()
56      {
57      }
58  
59      /* ------------------------------------------------------------ */
60      public FormAuthenticator(String login,String error)
61      {
62          if (login!=null)
63              setLoginPage(login);
64          if (error!=null)
65              setErrorPage(error);
66      }
67      
68      /* ------------------------------------------------------------ */
69      /**
70       * @see org.eclipse.jetty.security.authentication.LoginAuthenticator#setConfiguration(org.eclipse.jetty.security.Authenticator.Configuration)
71       */
72      @Override
73      public void setConfiguration(Configuration configuration)
74      {
75          super.setConfiguration(configuration);
76          String login=configuration.getInitParameter(FormAuthenticator.__FORM_LOGIN_PAGE);
77          if (login!=null)
78              setLoginPage(login);
79          String error=configuration.getInitParameter(FormAuthenticator.__FORM_ERROR_PAGE);
80          if (error!=null)
81              setErrorPage(error);
82          String dispatch=configuration.getInitParameter(FormAuthenticator.__FORM_DISPATCH);
83          _dispatch=dispatch!=null && Boolean.getBoolean(dispatch);
84      }
85  
86      public String getAuthMethod()
87      {
88          return Constraint.__FORM_AUTH;
89      }
90  
91      private void setLoginPage(String path)
92      {
93          if (!path.startsWith("/"))
94          {
95              Log.warn("form-login-page must start with /");
96              path = "/" + path;
97          }
98          _formLoginPage = path;
99          _formLoginPath = path;
100         if (_formLoginPath.indexOf('?') > 0) 
101             _formLoginPath = _formLoginPath.substring(0, _formLoginPath.indexOf('?'));
102     }
103 
104     /* ------------------------------------------------------------ */
105     private void setErrorPage(String path)
106     {
107         if (path == null || path.trim().length() == 0)
108         {
109             _formErrorPath = null;
110             _formErrorPage = null;
111         }
112         else
113         {
114             if (!path.startsWith("/"))
115             {
116                 Log.warn("form-error-page must start with /");
117                 path = "/" + path;
118             }
119             _formErrorPage = path;
120             _formErrorPath = path;
121 
122             if (_formErrorPath.indexOf('?') > 0) 
123                 _formErrorPath = _formErrorPath.substring(0, _formErrorPath.indexOf('?'));
124         }
125     }
126 
127     public Authentication validateRequest(ServletRequest req, ServletResponse res, boolean mandatory) throws ServerAuthException
128     {
129         HttpServletRequest request = (HttpServletRequest)req;
130         HttpServletResponse response = (HttpServletResponse)res;
131         HttpSession session = request.getSession(mandatory);
132         String uri = request.getPathInfo();
133         // not mandatory and not authenticated
134         if (session == null || isLoginOrErrorPage(uri)) 
135         {
136             return DefaultAuthentication.SUCCESS_UNAUTH_RESULTS;
137         }
138             
139 
140         try
141         {
142             // Handle a request for authentication.
143             // TODO perhaps j_securitycheck can be uri suffix?
144             if (uri.endsWith(__J_SECURITY_CHECK))
145             {
146                 final String username = request.getParameter(__J_USERNAME);
147                 final char[] password = request.getParameter(__J_PASSWORD).toCharArray();
148                 
149                 UserIdentity user = _loginService.login(username,password);
150                 if (user!=null)
151                 {
152                     // Redirect to original request
153                     String nuri = (String) session.getAttribute(__J_URI);
154                     if (nuri == null || nuri.length() == 0)
155                     {
156                         nuri = request.getContextPath();
157                         if (nuri.length() == 0) nuri = URIUtil.SLASH;
158                     }
159                     // TODO shouldn't we forward to original URI instead?
160                     session.removeAttribute(__J_URI); // Remove popped return URI.
161                     response.setContentLength(0);   
162                     response.sendRedirect(response.encodeRedirectURL(nuri));
163                     return new DefaultAuthentication(Authentication.Status.SEND_SUCCESS,Constraint.__FORM_AUTH,user);
164                 }
165                 
166                 // not authenticated
167                 if (Log.isDebugEnabled()) Log.debug("Form authentication FAILED for " + StringUtil.printable(username));
168                 if (_formErrorPage == null)
169                 {
170                     if (response != null) 
171                         response.sendError(HttpServletResponse.SC_FORBIDDEN);
172                 }
173                 else if (_dispatch)
174                 {
175                     RequestDispatcher dispatcher = request.getRequestDispatcher(_formErrorPage);
176                     response.setHeader(HttpHeaders.CACHE_CONTROL,"No-cache");
177                     response.setDateHeader(HttpHeaders.EXPIRES,1);
178                     dispatcher.forward(request, response);
179                 }
180                 else
181                 {
182                     response.sendRedirect(URIUtil.addPaths(request.getContextPath(),_formErrorPage));
183                 }
184                 
185                 // TODO is this correct response if isMandatory false??? Can
186                 // that occur?
187                 return DefaultAuthentication.SEND_FAILURE_RESULTS;
188             }
189             // Check if the session is already authenticated.
190 
191             // Don't authenticate authform or errorpage
192             if (!mandatory)
193             // TODO verify this is correct action
194                 return DefaultAuthentication.SUCCESS_UNAUTH_RESULTS;
195 
196             // redirect to login page
197             if (request.getQueryString() != null)
198                 uri += "?" + request.getQueryString();
199             //TODO is this safe if the client is sending several requests concurrently in the same session to secured resources?
200             session.setAttribute(__J_URI, request.getScheme() + "://"
201                                           + request.getServerName()
202                                           + ":"
203                                           + request.getServerPort()
204                                           + URIUtil.addPaths(request.getContextPath(), uri));
205             
206             if (_dispatch)
207             {
208                 RequestDispatcher dispatcher = request.getRequestDispatcher(_formLoginPage);
209                 response.setHeader(HttpHeaders.CACHE_CONTROL,"No-cache");
210                 response.setDateHeader(HttpHeaders.EXPIRES,1);
211                 dispatcher.forward(request, response);
212             }
213             else
214             {
215                 response.sendRedirect(URIUtil.addPaths(request.getContextPath(),_formLoginPage));
216             }
217             
218             return DefaultAuthentication.SEND_CONTINUE_RESULTS;
219         }
220         catch (IOException e)
221         {
222             throw new ServerAuthException(e);
223         }
224         catch (ServletException e)
225         {
226             throw new ServerAuthException(e);
227         }
228     }
229 
230     public boolean isLoginOrErrorPage(String pathInContext)
231     {
232         return pathInContext != null && (pathInContext.equals(_formErrorPath) || pathInContext.equals(_formLoginPath));
233     }
234 
235     public Authentication.Status secureResponse(ServletRequest req, ServletResponse res, boolean mandatory, Authentication validatedUser) throws ServerAuthException
236     {
237         return Authentication.Status.SUCCESS;
238     }
239 }