forked from ESAPI/esapi-java-legacy
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAbstractAuthenticator.java
More file actions
300 lines (256 loc) · 11.3 KB
/
AbstractAuthenticator.java
File metadata and controls
300 lines (256 loc) · 11.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
package org.owasp.esapi.reference;
import java.util.Date;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.owasp.esapi.ESAPI;
import org.owasp.esapi.HTTPUtilities;
import org.owasp.esapi.Logger;
import org.owasp.esapi.User;
import org.owasp.esapi.errors.AccessControlException;
import org.owasp.esapi.errors.AuthenticationCredentialsException;
import org.owasp.esapi.errors.AuthenticationException;
import org.owasp.esapi.errors.AuthenticationLoginException;
import org.owasp.esapi.errors.EnterpriseSecurityException;
/**
* A partial implementation of the Authenticator interface.
* This class should not implement any methods that would be meant
* to modify a User object, since that's probably implementation specific.
*
*/
public abstract class AbstractAuthenticator implements org.owasp.esapi.Authenticator {
/**
* Key for user in session
*/
protected static final String USER = "ESAPIUserSessionKey";
private final Logger logger = ESAPI.getLogger("Authenticator");
/**
* The currentUser ThreadLocal variable is used to make the currentUser available to any call in any part of an
* application. Otherwise, each thread would have to pass the User object through the calltree to any methods that
* need it. Because we want exceptions and log calls to contain user data, that could be almost anywhere. Therefore,
* the ThreadLocal approach simplifies things greatly. <P> As a possible extension, one could create a delegation
* framework by adding another ThreadLocal to hold the delegating user identity.
*/
private final ThreadLocalUser currentUser = new ThreadLocalUser();
private class ThreadLocalUser extends InheritableThreadLocal<User> {
public User initialValue() {
return User.ANONYMOUS;
}
public User getUser() {
return super.get();
}
public void setUser(User newUser) {
super.set(newUser);
}
}
/**
*
*/
public AbstractAuthenticator() {
super();
}
/**
* {@inheritDoc}
*/
public void clearCurrent() {
// logger.logWarning(Logger.SECURITY, "************Clearing threadlocals. Thread" + Thread.currentThread().getName() );
currentUser.setUser(null);
}
/**
* {@inheritDoc}
*/
public boolean exists(String accountName) {
return getUser(accountName) != null;
}
/**
* {@inheritDoc}
* <p/>
* Returns the currently logged user as set by the setCurrentUser() methods. Must not log in this method because the
* logger calls getCurrentUser() and this could cause a loop.
*/
public User getCurrentUser() {
User user = currentUser.get();
if (user == null) {
user = User.ANONYMOUS;
}
return user;
}
/**
* Gets the user from session.
*
* @return the user from session or null if no user is found in the session
*/
protected User getUserFromSession() {
HTTPUtilities httpUtils = ESAPI.httpUtilities();
HttpServletRequest req = httpUtils.getCurrentRequest();
HttpSession session = req.getSession(false);
if (session == null) return null;
return ESAPI.httpUtilities().getSessionAttribute(USER);
}
/**
* Returns the user if a matching remember token is found, or null if the token
* is missing, token is corrupt, token is expired, account name does not match
* and existing account, or hashed password does not match user's hashed password.
*
* @return the user if a matching remember token is found, or null if the token
* is missing, token is corrupt, token is expired, account name does not match
* and existing account, or hashed password does not match user's hashed password.
*/
protected DefaultUser getUserFromRememberToken() {
try {
HTTPUtilities utils =ESAPI.httpUtilities();
String token = utils.getCookie(ESAPI.currentRequest(), HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME);
if (token == null) return null;
// See Google Issue 144 regarding first URLDecode the token and THEN unsealing.
// Note that this Google Issue was marked as "WontFix".
String[] data = ESAPI.encryptor().unseal(token).split("\\|");
if (data.length != 2) {
logger.warning(Logger.SECURITY_FAILURE, "Found corrupt or expired remember token");
ESAPI.httpUtilities().killCookie(ESAPI.currentRequest(), ESAPI.currentResponse(), HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME);
return null;
}
String username = data[0];
String password = data[1];
DefaultUser user = (DefaultUser) getUser(username);
if (user == null) {
logger.warning(Logger.SECURITY_FAILURE, "Found valid remember token but no user matching " + username);
return null;
}
logger.info(Logger.SECURITY_SUCCESS, "Logging in user with remember token: " + user.getAccountName());
user.loginWithPassword(password);
return user;
} catch (AuthenticationException ae) {
logger.warning(Logger.SECURITY_FAILURE, "Login via remember me cookie failed", ae);
} catch (EnterpriseSecurityException e) {
logger.warning(Logger.SECURITY_FAILURE, "Remember token was missing, corrupt, or expired");
}
ESAPI.httpUtilities().killCookie(ESAPI.currentRequest(), ESAPI.currentResponse(), HTTPUtilities.REMEMBER_TOKEN_COOKIE_NAME);
return null;
}
/**
* Utility method to extract credentials and verify them.
*
* @param request The current HTTP request
* @return The user that successfully authenticated
* @throws AuthenticationException if the submitted credentials are invalid.
*/
private User loginWithUsernameAndPassword(HttpServletRequest request) throws AuthenticationException {
String username = request.getParameter(ESAPI.securityConfiguration().getUsernameParameterName());
String password = request.getParameter(ESAPI.securityConfiguration().getPasswordParameterName());
// if a logged-in user is requesting to login, log them out first
User user = getCurrentUser();
if (user != null && !user.isAnonymous()) {
logger.warning(Logger.SECURITY_SUCCESS, "User requested relogin. Performing logout then authentication");
user.logout();
}
// now authenticate with username and password
if (username == null || password == null) {
if (username == null) {
username = "unspecified user";
}
throw new AuthenticationCredentialsException("Authentication failed", "Authentication failed for " + username + " because of null username or password");
}
user = getUser(username);
if (user == null) {
throw new AuthenticationCredentialsException("Authentication failed", "Authentication failed because user " + username + " doesn't exist");
}
user.loginWithPassword(password);
request.setAttribute(user.getCSRFToken(), "authenticated");
return user;
}
/**
* {@inheritDoc}
*/
public User login() throws AuthenticationException {
return login(ESAPI.currentRequest(), ESAPI.currentResponse());
}
/**
* {@inheritDoc}
*/
public User login(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (request == null || response == null) {
throw new AuthenticationCredentialsException("Invalid request", "Request or response objects were null");
}
// if there's a user in the session then use that
DefaultUser user = (DefaultUser) getUserFromSession();
// else if there's a remember token then use that
if (user == null) {
user = getUserFromRememberToken();
}
// else try to verify credentials - throws exception if login fails
if (user == null) {
user = (DefaultUser) loginWithUsernameAndPassword(request);
}
// set last host address
user.setLastHostAddress(request.getRemoteHost());
// warn if this authentication request was not POST or non-SSL connection, exposing credentials or session id
try {
ESAPI.httpUtilities().assertSecureRequest(ESAPI.currentRequest());
} catch (AccessControlException e) {
throw new AuthenticationException("Attempt to login with an insecure request", e.getLogMessage(), e);
}
// don't let anonymous user log in
if (user.isAnonymous()) {
user.logout();
throw new AuthenticationLoginException("Login failed", "Anonymous user cannot be set to current user. User: " + user.getAccountName());
}
// don't let disabled users log in
if (!user.isEnabled()) {
user.logout();
user.incrementFailedLoginCount();
user.setLastFailedLoginTime(new Date());
throw new AuthenticationLoginException("Login failed", "Disabled user cannot be set to current user. User: " + user.getAccountName());
}
// don't let locked users log in
if (user.isLocked()) {
user.logout();
user.incrementFailedLoginCount();
user.setLastFailedLoginTime(new Date());
throw new AuthenticationLoginException("Login failed", "Locked user cannot be set to current user. User: " + user.getAccountName());
}
// don't let expired users log in
if (user.isExpired()) {
user.logout();
user.incrementFailedLoginCount();
user.setLastFailedLoginTime(new Date());
throw new AuthenticationLoginException("Login failed", "Expired user cannot be set to current user. User: " + user.getAccountName());
}
// check session inactivity timeout
if (user.isSessionTimeout()) {
user.logout();
user.incrementFailedLoginCount();
user.setLastFailedLoginTime(new Date());
throw new AuthenticationLoginException("Login failed", "Session inactivity timeout: " + user.getAccountName());
}
// check session absolute timeout
if (user.isSessionAbsoluteTimeout()) {
user.logout();
user.incrementFailedLoginCount();
user.setLastFailedLoginTime(new Date());
throw new AuthenticationLoginException("Login failed", "Session absolute timeout: " + user.getAccountName());
}
//set Locale to the user object in the session from request
user.setLocale(request.getLocale());
// create new session for this User
HttpSession session = request.getSession();
user.addSession(session);
session.setAttribute(USER, user);
setCurrentUser(user);
return user;
}
/**
* {@inheritDoc}
*/
public void logout() {
User user = getCurrentUser();
if (user != null && !user.isAnonymous()) {
user.logout();
}
}
/**
* {@inheritDoc}
*/
public void setCurrentUser(User user) {
currentUser.setUser(user);
}
}