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 public FormAuthModule(CallbackHandler callbackHandler, CrossContextPsuedoSession<UserInfo> ssoSource,
96 String loginPage, String errorPage)
97 {
98 super(callbackHandler);
99 this.ssoSource = ssoSource;
100 setLoginPage(loginPage);
101 setErrorPage(errorPage);
102 }
103
104 @Override
105 public void initialize(MessagePolicy requestPolicy, MessagePolicy responsePolicy,
106 CallbackHandler handler, Map options)
107 throws AuthException
108 {
109 super.initialize(requestPolicy, responsePolicy, handler, options);
110 setLoginPage((String) options.get(LOGIN_PAGE_KEY));
111 setErrorPage((String) options.get(ERROR_PAGE_KEY));
112 ssoSource = (CrossContextPsuedoSession<UserInfo>) options.get(SSO_SOURCE_KEY);
113 }
114
115 private void setLoginPage(String path)
116 {
117 if (!path.startsWith("/"))
118 {
119 LOG.warn("form-login-page must start with /");
120 path = "/" + path;
121 }
122 _formLoginPage = path;
123 _formLoginPath = path;
124 if (_formLoginPath.indexOf('?') > 0) _formLoginPath = _formLoginPath.substring(0, _formLoginPath.indexOf('?'));
125 }
126
127
128 private void setErrorPage(String path)
129 {
130 if (path == null || path.trim().length() == 0)
131 {
132 _formErrorPath = null;
133 _formErrorPage = null;
134 }
135 else
136 {
137 if (!path.startsWith("/"))
138 {
139 LOG.warn("form-error-page must start with /");
140 path = "/" + path;
141 }
142 _formErrorPage = path;
143 _formErrorPath = path;
144
145 if (_formErrorPath.indexOf('?') > 0) _formErrorPath = _formErrorPath.substring(0, _formErrorPath.indexOf('?'));
146 }
147 }
148
149 @Override
150 public AuthStatus validateRequest(MessageInfo messageInfo, Subject clientSubject, Subject serviceSubject) throws AuthException
151 {
152
153 HttpServletRequest request = (HttpServletRequest) messageInfo.getRequestMessage();
154 HttpServletResponse response = (HttpServletResponse) messageInfo.getResponseMessage();
155 String uri = request.getRequestURI();
156 if (uri==null)
157 uri=URIUtil.SLASH;
158
159 boolean mandatory = isMandatory(messageInfo);
160 mandatory |= isJSecurityCheck(uri);
161 HttpSession session = request.getSession(mandatory);
162
163
164 if (!mandatory || isLoginOrErrorPage(URIUtil.addPaths(request.getServletPath(),request.getPathInfo()))) return AuthStatus.SUCCESS;
165
166 try
167 {
168
169 if (isJSecurityCheck(uri))
170 {
171 final String username = request.getParameter(__J_USERNAME);
172 final String password = request.getParameter(__J_PASSWORD);
173
174 boolean success = tryLogin(messageInfo, clientSubject, response, session, username, new Password(password));
175 if (success)
176 {
177
178 String nuri=null;
179 synchronized(session)
180 {
181 nuri = (String) session.getAttribute(__J_URI);
182 }
183
184 if (nuri == null || nuri.length() == 0)
185 {
186 nuri = request.getContextPath();
187 if (nuri.length() == 0)
188 nuri = URIUtil.SLASH;
189 }
190
191 response.setContentLength(0);
192 response.sendRedirect(response.encodeRedirectURL(nuri));
193 return AuthStatus.SEND_CONTINUE;
194 }
195
196 if (LOG.isDebugEnabled()) LOG.debug("Form authentication FAILED for " + StringUtil.printable(username));
197 if (_formErrorPage == null)
198 {
199 if (response != null) response.sendError(HttpServletResponse.SC_FORBIDDEN);
200 }
201 else
202 {
203 response.setContentLength(0);
204 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), _formErrorPage)));
205 }
206
207
208 return AuthStatus.SEND_FAILURE;
209 }
210
211
212
213 SessionAuthentication sessionAuth = (SessionAuthentication)session.getAttribute(SessionAuthentication.__J_AUTHENTICATED);
214 if (sessionAuth != null)
215 {
216
217
218
219 if (sessionAuth.getUserIdentity().getSubject() == null)
220 return AuthStatus.SEND_FAILURE;
221
222 Set<Object> credentials = sessionAuth.getUserIdentity().getSubject().getPrivateCredentials();
223 if (credentials == null || credentials.isEmpty())
224 return AuthStatus.SEND_FAILURE;
225
226 clientSubject.getPrivateCredentials().addAll(credentials);
227 clientSubject.getPrivateCredentials().add(sessionAuth.getUserIdentity());
228
229 return AuthStatus.SUCCESS;
230 }
231 else if (ssoSource != null)
232 {
233 UserInfo userInfo = ssoSource.fetch(request);
234 if (userInfo != null)
235 {
236 boolean success = tryLogin(messageInfo, clientSubject, response, session, userInfo.getUserName(), new Password(new String(userInfo.getPassword())));
237 if (success) { return AuthStatus.SUCCESS; }
238 }
239 }
240
241
242
243
244 if (DeferredAuthentication.isDeferred(response))
245 return AuthStatus.SUCCESS;
246
247
248
249 StringBuffer buf = request.getRequestURL();
250 if (request.getQueryString() != null)
251 buf.append("?").append(request.getQueryString());
252
253 synchronized (session)
254 {
255 session.setAttribute(__J_URI, buf.toString());
256 }
257
258 response.setContentLength(0);
259 response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), _formLoginPage)));
260 return AuthStatus.SEND_CONTINUE;
261 }
262 catch (IOException e)
263 {
264 throw new AuthException(e.getMessage());
265 }
266 catch (UnsupportedCallbackException e)
267 {
268 throw new AuthException(e.getMessage());
269 }
270
271 }
272
273
274 public boolean isJSecurityCheck(String uri)
275 {
276 int jsc = uri.indexOf(__J_SECURITY_CHECK);
277
278 if (jsc<0)
279 return false;
280 int e=jsc+__J_SECURITY_CHECK.length();
281 if (e==uri.length())
282 return true;
283 char c = uri.charAt(e);
284 return c==';'||c=='#'||c=='/'||c=='?';
285 }
286
287 private boolean tryLogin(MessageInfo messageInfo, Subject clientSubject,
288 HttpServletResponse response, HttpSession session,
289 String username, Password password)
290 throws AuthException, IOException, UnsupportedCallbackException
291 {
292 if (login(clientSubject, username, password, Constraint.__FORM_AUTH, messageInfo))
293 {
294 char[] pwdChars = password.toString().toCharArray();
295 Set<LoginCallbackImpl> loginCallbacks = clientSubject.getPrivateCredentials(LoginCallbackImpl.class);
296
297 if (!loginCallbacks.isEmpty())
298 {
299 LoginCallbackImpl loginCallback = loginCallbacks.iterator().next();
300 Set<UserIdentity> userIdentities = clientSubject.getPrivateCredentials(UserIdentity.class);
301 if (!userIdentities.isEmpty())
302 {
303 UserIdentity userIdentity = userIdentities.iterator().next();
304
305 SessionAuthentication sessionAuth = new SessionAuthentication(Constraint.__FORM_AUTH, userIdentity, password);
306 session.setAttribute(SessionAuthentication.__J_AUTHENTICATED, sessionAuth);
307 }
308 }
309
310
311 if (ssoSource != null)
312 {
313 UserInfo userInfo = new UserInfo(username, pwdChars);
314 ssoSource.store(userInfo, response);
315 }
316 return true;
317 }
318 return false;
319 }
320
321 public boolean isLoginOrErrorPage(String pathInContext)
322 {
323 return pathInContext != null && (pathInContext.equals(_formErrorPath) || pathInContext.equals(_formLoginPath));
324 }
325
326 }