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.util.Map;
23 import java.util.Set;
24
25 import javax.security.auth.Subject;
26 import javax.security.auth.callback.CallbackHandler;
27 import javax.security.auth.callback.UnsupportedCallbackException;
28 import javax.security.auth.message.AuthException;
29 import javax.security.auth.message.AuthStatus;
30 import javax.security.auth.message.MessageInfo;
31 import javax.security.auth.message.MessagePolicy;
32 import javax.servlet.http.HttpServletRequest;
33 import javax.servlet.http.HttpServletResponse;
34 import javax.servlet.http.HttpSession;
35
36 import org.eclipse.jetty.security.CrossContextPsuedoSession;
37 import org.eclipse.jetty.security.authentication.DeferredAuthentication;
38 import org.eclipse.jetty.security.authentication.LoginCallbackImpl;
39 import org.eclipse.jetty.security.authentication.SessionAuthentication;
40 import org.eclipse.jetty.server.UserIdentity;
41 import org.eclipse.jetty.util.StringUtil;
42 import org.eclipse.jetty.util.URIUtil;
43 import org.eclipse.jetty.util.log.Log;
44 import org.eclipse.jetty.util.log.Logger;
45 import org.eclipse.jetty.util.security.Constraint;
46 import org.eclipse.jetty.util.security.Password;
47
48
49
50
51
52 public class FormAuthModule extends BaseAuthModule
53 {
54 private static final Logger LOG = Log.getLogger(FormAuthModule.class);
55
56
57 public final static String __J_URI = "org.eclipse.jetty.util.URI";
58
59 public final static String __J_AUTHENTICATED = "org.eclipse.jetty.server.Auth";
60
61 public final static String __J_SECURITY_CHECK = "/j_security_check";
62
63 public final static String __J_USERNAME = "j_username";
64
65 public final static String __J_PASSWORD = "j_password";
66
67
68 public static final String LOGIN_PAGE_KEY = "org.eclipse.jetty.security.jaspi.modules.LoginPage";
69
70 public static final String ERROR_PAGE_KEY = "org.eclipse.jetty.security.jaspi.modules.ErrorPage";
71
72 public static final String SSO_SOURCE_KEY = "org.eclipse.jetty.security.jaspi.modules.SsoSource";
73
74 private String _formErrorPage;
75
76 private String _formErrorPath;
77
78 private String _formLoginPage;
79
80 private String _formLoginPath;
81
82 private CrossContextPsuedoSession<UserInfo> ssoSource;
83
84 public FormAuthModule()
85 {
86 }
87
88 public FormAuthModule(CallbackHandler callbackHandler, String loginPage, String errorPage)
89 {
90 super(callbackHandler);
91 setLoginPage(loginPage);
92 setErrorPage(errorPage);
93 }
94
95
96
97
98 public FormAuthModule(CallbackHandler callbackHandler, CrossContextPsuedoSession<UserInfo> ssoSource,
99 String loginPage, String errorPage)
100 {
101 super(callbackHandler);
102 this.ssoSource = ssoSource;
103 setLoginPage(loginPage);
104 setErrorPage(errorPage);
105 }
106
107 @Override
108 public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy,
109 CallbackHandler handler, Map options)
110 throws AuthException
111 {
112 super.initialize(requestPolicy, responsePolicy, handler, options);
113 setLoginPage((String) options.get(LOGIN_PAGE_KEY));
114 setErrorPage((String) options.get(ERROR_PAGE_KEY));
115 ssoSource = (CrossContextPsuedoSession<UserInfo>) options.get(SSO_SOURCE_KEY);
116 }
117
118 private void setLoginPage(String path)
119 {
120 if (!path.startsWith("/"))
121 {
122 LOG.warn("form-login-page must start with /");
123 path = "/" + path;
124 }
125 _formLoginPage = path;
126 _formLoginPath = path;
127 if (_formLoginPath.indexOf('?') > 0) _formLoginPath = _formLoginPath.substring(0, _formLoginPath.indexOf('?'));
128 }
129
130
131 private void setErrorPage(String path)
132 {
133 if (path == null || path.trim().length() == 0)
134 {
135 _formErrorPath = null;
136 _formErrorPage = null;
137 }
138 else
139 {
140 if (!path.startsWith("/"))
141 {
142 LOG.warn("form-error-page must start with /");
143 path = "/" + path;
144 }
145 _formErrorPage = path;
146 _formErrorPath = path;
147
148 if (_formErrorPath.indexOf('?') > 0) _formErrorPath = _formErrorPath.substring(0, _formErrorPath.indexOf('?'));
149 }
150 }
151
152 @Override
153 public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException
154 {
155
156 HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();
157 HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage();
158 String uri = request.getRequestURI();
159 if (uri==null)
160 uri=URIUtil.SLASH;
161
162 boolean mandatory = isMandatory(messageInfo);
163 mandatory |= isJSecurityCheck(uri);
164 HttpSession session = request.getSession(mandatory);
165
166
167 if (!mandatory || isLoginOrErrorPage(URIUtil.addPaths(request.getServletPath(),request.getPathInfo())))
168 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 SessionAuthentication sessionAuth = (SessionAuthentication)session.getAttribute(SessionAuthentication.__J_AUTHENTICATED);
218 if (sessionAuth != null)
219 {
220
221
222
223 if (sessionAuth.getUserIdentity().getSubject() == null)
224 return AuthStatus.SEND_FAILURE;
225
226 Set<Object> credentials = sessionAuth.getUserIdentity().getSubject().getPrivateCredentials();
227 if (credentials == null || credentials.isEmpty())
228 return AuthStatus.SEND_FAILURE;
229
230 clientSubject.getPrivateCredentials().addAll(credentials);
231 clientSubject.getPrivateCredentials().add(sessionAuth.getUserIdentity());
232
233 return AuthStatus.SUCCESS;
234 }
235 else if (ssoSource != null)
236 {
237 UserInfo userInfo = ssoSource.fetch(request);
238 if (userInfo != null)
239 {
240 boolean success = tryLogin(messageInfo, clientSubject, response, session, userInfo.getUserName(), new Password(new String(userInfo.getPassword())));
241 if (success) { return AuthStatus.SUCCESS; }
242 }
243 }
244
245
246
247
248 if (DeferredAuthentication.isDeferred(response))
249 return AuthStatus.SUCCESS;
250
251
252
253 StringBuffer buf = request.getRequestURL();
254 if (request.getQueryString() != null)
255 buf.append("?").append(request.getQueryString());
256
257 synchronized (session)
258 {
259 session.setAttribute(__J_URI, buf.toString());
260 }
261
262 response.setContentLength(0);
263 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), _formLoginPage)));
264 return AuthStatus.SEND_CONTINUE;
265 }
266 catch (IOException e)
267 {
268 throw new AuthException(e.getMessage());
269 }
270 catch (UnsupportedCallbackException e)
271 {
272 throw new AuthException(e.getMessage());
273 }
274
275 }
276
277
278 public boolean isJSecurityCheck(String uri)
279 {
280 int jsc = uri.indexOf(__J_SECURITY_CHECK);
281
282 if (jsc<0)
283 return false;
284 int e=jsc+__J_SECURITY_CHECK.length();
285 if (e==uri.length())
286 return true;
287 char c = uri.charAt(e);
288 return c==';'||c=='#'||c=='/'||c=='?';
289 }
290
291 private boolean tryLogin(MessageInfo messageInfo, Subject clientSubject,
292 HttpServletResponse response, HttpSession session,
293 String username, Password password)
294 throws AuthException, IOException, UnsupportedCallbackException
295 {
296 if (login(clientSubject, username, password, Constraint.__FORM_AUTH, messageInfo))
297 {
298 char[] pwdChars = password.toString().toCharArray();
299 Set<LoginCallbackImpl> loginCallbacks = clientSubject.getPrivateCredentials(LoginCallbackImpl.class);
300
301 if (!loginCallbacks.isEmpty())
302 {
303 LoginCallbackImpl loginCallback = loginCallbacks.iterator().next();
304 Set<UserIdentity> userIdentities = clientSubject.getPrivateCredentials(UserIdentity.class);
305 if (!userIdentities.isEmpty())
306 {
307 UserIdentity userIdentity = userIdentities.iterator().next();
308
309 SessionAuthentication sessionAuth = new SessionAuthentication(Constraint.__FORM_AUTH, userIdentity, password);
310 session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, sessionAuth);
311 }
312 }
313
314
315 if (ssoSource != null)
316 {
317 UserInfo userInfo = new UserInfo(username, pwdChars);
318 ssoSource.store(userInfo, response);
319 }
320 return true;
321 }
322 return false;
323 }
324
325 public boolean isLoginOrErrorPage(String pathInContext)
326 {
327 return pathInContext != null && (pathInContext.equals(_formErrorPath) || pathInContext.equals(_formLoginPath));
328 }
329
330 }