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()))) return AuthStatus.SUCCESS;
168
169 try
170 {
171
172 if (isJSecurityCheck(uri))
173 {
174 final String username = request.getParameter(__J_USERNAME);
175 final String password = request.getParameter(__J_PASSWORD);
176
177 boolean success = tryLogin(messageInfo, clientSubject, response, session, username, new Password(password));
178 if (success)
179 {
180
181 String nuri=null;
182 synchronized(session)
183 {
184 nuri = (String) session.getAttribute(__J_URI);
185 }
186
187 if (nuri == null || nuri.length() == 0)
188 {
189 nuri = request.getContextPath();
190 if (nuri.length() == 0)
191 nuri = URIUtil.SLASH;
192 }
193
194 response.setContentLength(0);
195 response.sendRedirect(response.encodeRedirectURL(nuri));
196 return AuthStatus.SEND_CONTINUE;
197 }
198
199 if (LOG.isDebugEnabled()) LOG.debug("Form authentication FAILED for " + StringUtil.printable(username));
200 if (_formErrorPage == null)
201 {
202 if (response != null) response.sendError(HttpServletResponse.SC_FORBIDDEN);
203 }
204 else
205 {
206 response.setContentLength(0);
207 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), _formErrorPage)));
208 }
209
210
211 return AuthStatus.SEND_FAILURE;
212 }
213
214
215
216 SessionAuthentication sessionAuth = (SessionAuthentication)session.getAttribute(SessionAuthentication.__J_AUTHENTICATED);
217 if (sessionAuth != null)
218 {
219
220
221
222 if (sessionAuth.getUserIdentity().getSubject() == null)
223 return AuthStatus.SEND_FAILURE;
224
225 Set<Object> credentials = sessionAuth.getUserIdentity().getSubject().getPrivateCredentials();
226 if (credentials == null || credentials.isEmpty())
227 return AuthStatus.SEND_FAILURE;
228
229 clientSubject.getPrivateCredentials().addAll(credentials);
230 clientSubject.getPrivateCredentials().add(sessionAuth.getUserIdentity());
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 Set<UserIdentity> userIdentities = clientSubject.getPrivateCredentials(UserIdentity.class);
304 if (!userIdentities.isEmpty())
305 {
306 UserIdentity userIdentity = userIdentities.iterator().next();
307
308 SessionAuthentication sessionAuth = new SessionAuthentication(Constraint.__FORM_AUTH, userIdentity, password);
309 session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, sessionAuth);
310 }
311 }
312
313
314 if (ssoSource != null)
315 {
316 UserInfo userInfo = new UserInfo(username, pwdChars);
317 ssoSource.store(userInfo, response);
318 }
319 return true;
320 }
321 return false;
322 }
323
324 public boolean isLoginOrErrorPage(String pathInContext)
325 {
326 return pathInContext != null && (pathInContext.equals(_formErrorPath) || pathInContext.equals(_formLoginPath));
327 }
328
329 }