1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.security.jaspi.modules;
20
21 import java.io.IOException;
22 import java.io.Serializable;
23 import java.security.Principal;
24 import java.util.Arrays;
25 import java.util.Map;
26 import java.util.Set;
27
28 import javax.security.auth.Subject;
29 import javax.security.auth.callback.CallbackHandler;
30 import javax.security.auth.callback.UnsupportedCallbackException;
31 import javax.security.auth.message.AuthException;
32 import javax.security.auth.message.AuthStatus;
33 import javax.security.auth.message.MessageInfo;
34 import javax.security.auth.message.MessagePolicy;
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37 import javax.servlet.http.HttpSession;
38 import javax.servlet.http.HttpSessionBindingEvent;
39 import javax.servlet.http.HttpSessionBindingListener;
40
41 import org.eclipse.jetty.util.security.Constraint;
42 import org.eclipse.jetty.util.security.Password;
43 import org.eclipse.jetty.security.CrossContextPsuedoSession;
44 import org.eclipse.jetty.security.authentication.DeferredAuthentication;
45 import org.eclipse.jetty.security.authentication.LoginCallbackImpl;
46 import org.eclipse.jetty.server.Authentication;
47 import org.eclipse.jetty.util.StringUtil;
48 import org.eclipse.jetty.util.URIUtil;
49 import org.eclipse.jetty.util.log.Log;
50 import org.eclipse.jetty.util.log.Logger;
51
52
53
54
55
56 public class FormAuthModule extends BaseAuthModule
57 {
58 private static final Logger LOG = Log.getLogger(FormAuthModule.class);
59
60
61 public final static String __J_URI = "org.eclipse.jetty.util.URI";
62
63 public final static String __J_AUTHENTICATED = "org.eclipse.jetty.server.Auth";
64
65 public final static String __J_SECURITY_CHECK = "/j_security_check";
66
67 public final static String __J_USERNAME = "j_username";
68
69 public final static String __J_PASSWORD = "j_password";
70
71
72 public static final String LOGIN_PAGE_KEY = "org.eclipse.jetty.security.jaspi.modules.LoginPage";
73
74 public static final String ERROR_PAGE_KEY = "org.eclipse.jetty.security.jaspi.modules.ErrorPage";
75
76 public static final String SSO_SOURCE_KEY = "org.eclipse.jetty.security.jaspi.modules.SsoSource";
77
78 private String _formErrorPage;
79
80 private String _formErrorPath;
81
82 private String _formLoginPage;
83
84 private String _formLoginPath;
85
86 private CrossContextPsuedoSession<UserInfo> ssoSource;
87
88 public FormAuthModule()
89 {
90 }
91
92 public FormAuthModule(CallbackHandler callbackHandler, String loginPage, String errorPage)
93 {
94 super(callbackHandler);
95 setLoginPage(loginPage);
96 setErrorPage(errorPage);
97 }
98
99 public FormAuthModule(CallbackHandler callbackHandler, CrossContextPsuedoSession<UserInfo> ssoSource,
100 String loginPage, String errorPage)
101 {
102 super(callbackHandler);
103 this.ssoSource = ssoSource;
104 setLoginPage(loginPage);
105 setErrorPage(errorPage);
106 }
107
108 @Override
109 public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy,
110 CallbackHandler handler, Map options)
111 throws AuthException
112 {
113 super.initialize(requestPolicy, responsePolicy, handler, options);
114 setLoginPage((String) options.get(LOGIN_PAGE_KEY));
115 setErrorPage((String) options.get(ERROR_PAGE_KEY));
116 ssoSource = (CrossContextPsuedoSession<UserInfo>) options.get(SSO_SOURCE_KEY);
117 }
118
119 private void setLoginPage(String path)
120 {
121 if (!path.startsWith("/"))
122 {
123 LOG.warn("form-login-page must start with /");
124 path = "/" + path;
125 }
126 _formLoginPage = path;
127 _formLoginPath = path;
128 if (_formLoginPath.indexOf('?') > 0) _formLoginPath = _formLoginPath.substring(0, _formLoginPath.indexOf('?'));
129 }
130
131
132 private void setErrorPage(String path)
133 {
134 if (path == null || path.trim().length() == 0)
135 {
136 _formErrorPath = null;
137 _formErrorPage = null;
138 }
139 else
140 {
141 if (!path.startsWith("/"))
142 {
143 LOG.warn("form-error-page must start with /");
144 path = "/" + path;
145 }
146 _formErrorPage = path;
147 _formErrorPath = path;
148
149 if (_formErrorPath.indexOf('?') > 0) _formErrorPath = _formErrorPath.substring(0, _formErrorPath.indexOf('?'));
150 }
151 }
152
153 @Override
154 public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException
155 {
156
157 HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();
158 HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage();
159 String uri = request.getRequestURI();
160 if (uri==null)
161 uri=URIUtil.SLASH;
162
163 boolean mandatory = isMandatory(messageInfo);
164 mandatory |= isJSecurityCheck(uri);
165 HttpSession session = request.getSession(mandatory);
166
167
168 if (!mandatory || isLoginOrErrorPage(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()))) return AuthStatus.SUCCESS;
169
170 try
171 {
172
173 if (isJSecurityCheck(uri))
174 {
175 final String username = request.getParameter(__J_USERNAME);
176 final String password = request.getParameter(__J_PASSWORD);
177
178 boolean success = tryLogin(messageInfo, clientSubject, response, session, username, new Password(password));
179 if (success)
180 {
181
182 String nuri=null;
183 synchronized(session)
184 {
185 nuri = (String) session.getAttribute(__J_URI);
186 }
187
188 if (nuri == null || nuri.length() == 0)
189 {
190 nuri = request.getContextPath();
191 if (nuri.length() == 0)
192 nuri = URIUtil.SLASH;
193 }
194
195 response.setContentLength(0);
196 response.sendRedirect(response.encodeRedirectURL(nuri));
197 return AuthStatus.SEND_CONTINUE;
198 }
199
200 if (LOG.isDebugEnabled()) LOG.debug("Form authentication FAILED for " + StringUtil.printable(username));
201 if (_formErrorPage == null)
202 {
203 if (response != null) response.sendError(HttpServletResponse.SC_FORBIDDEN);
204 }
205 else
206 {
207 response.setContentLength(0);
208 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), _formErrorPage)));
209 }
210
211
212 return AuthStatus.SEND_FAILURE;
213 }
214
215
216
217 FormCredential form_cred = (FormCredential) session.getAttribute(__J_AUTHENTICATED);
218 if (form_cred != null)
219 {
220
221
222
223 if (form_cred._subject == null)
224 return AuthStatus.SEND_FAILURE;
225 Set<Object> credentials = form_cred._subject.getPrivateCredentials();
226 if (credentials == null || credentials.isEmpty())
227 return AuthStatus.SEND_FAILURE;
228
229 clientSubject.getPrivateCredentials().addAll(credentials);
230
231
232 return AuthStatus.SUCCESS;
233 }
234 else if (ssoSource != null)
235 {
236 UserInfo userInfo = ssoSource.fetch(request);
237 if (userInfo != null)
238 {
239 boolean success = tryLogin(messageInfo, clientSubject, response, session, userInfo.getUserName(), new Password(new String(userInfo.getPassword())));
240 if (success) { return AuthStatus.SUCCESS; }
241 }
242 }
243
244
245
246
247 if (DeferredAuthentication.isDeferred(response))
248 return AuthStatus.SUCCESS;
249
250
251
252 StringBuffer buf = request.getRequestURL();
253 if (request.getQueryString() != null)
254 buf.append("?").append(request.getQueryString());
255
256 synchronized (session)
257 {
258 session.setAttribute(__J_URI, buf.toString());
259 }
260
261 response.setContentLength(0);
262 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), _formLoginPage)));
263 return AuthStatus.SEND_CONTINUE;
264 }
265 catch (IOException e)
266 {
267 throw new AuthException(e.getMessage());
268 }
269 catch (UnsupportedCallbackException e)
270 {
271 throw new AuthException(e.getMessage());
272 }
273
274 }
275
276
277 public boolean isJSecurityCheck(String uri)
278 {
279 int jsc = uri.indexOf(__J_SECURITY_CHECK);
280
281 if (jsc<0)
282 return false;
283 int e=jsc+__J_SECURITY_CHECK.length();
284 if (e==uri.length())
285 return true;
286 char c = uri.charAt(e);
287 return c==';'||c=='#'||c=='/'||c=='?';
288 }
289
290 private boolean tryLogin(MessageInfo messageInfo, Subject clientSubject,
291 HttpServletResponse response, HttpSession session,
292 String username, Password password)
293 throws AuthException, IOException, UnsupportedCallbackException
294 {
295 if (login(clientSubject, username, password, Constraint.__FORM_AUTH, messageInfo))
296 {
297 char[] pwdChars = password.toString().toCharArray();
298 Set<LoginCallbackImpl> loginCallbacks = clientSubject.getPrivateCredentials(LoginCallbackImpl.class);
299
300 if (!loginCallbacks.isEmpty())
301 {
302 LoginCallbackImpl loginCallback = loginCallbacks.iterator().next();
303 FormCredential form_cred = new FormCredential(username, pwdChars, loginCallback.getUserPrincipal(), loginCallback.getSubject());
304 session.setAttribute(__J_AUTHENTICATED, form_cred);
305 }
306
307
308 if (ssoSource != null)
309 {
310 UserInfo userInfo = new UserInfo(username, pwdChars);
311 ssoSource.store(userInfo, response);
312 }
313 return true;
314 }
315 return false;
316 }
317
318 public boolean isLoginOrErrorPage(String pathInContext)
319 {
320 return pathInContext != null && (pathInContext.equals(_formErrorPath) || pathInContext.equals(_formLoginPath));
321 }
322
323
324
325
326
327 private static class FormCredential implements Serializable, HttpSessionBindingListener
328 {
329 String _jUserName;
330
331 char[] _jPassword;
332
333 transient Principal _userPrincipal;
334
335 transient Subject _subject;
336
337 private FormCredential(String _jUserName, char[] _jPassword, Principal _userPrincipal, Subject subject)
338 {
339 this._jUserName = _jUserName;
340 this._jPassword = _jPassword;
341 this._userPrincipal = _userPrincipal;
342 this._subject = subject;
343 }
344
345 public void valueBound(HttpSessionBindingEvent event)
346 {
347 }
348
349 public void valueUnbound(HttpSessionBindingEvent event)
350 {
351 if (LOG.isDebugEnabled()) LOG.debug("Logout " + _jUserName);
352
353
354
355
356
357
358
359 }
360
361 public int hashCode()
362 {
363 return _jUserName.hashCode() + _jPassword.hashCode();
364 }
365
366 public boolean equals(Object o)
367 {
368 if (!(o instanceof FormCredential)) return false;
369 FormCredential fc = (FormCredential) o;
370 return _jUserName.equals(fc._jUserName) && Arrays.equals(_jPassword, fc._jPassword);
371 }
372
373 public String toString()
374 {
375 return "Cred[" + _jUserName + "]";
376 }
377
378 }
379
380 }