-
-
Notifications
You must be signed in to change notification settings - Fork 190
Refactor PersistentLogin for readability and maintainability #5460
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop-6.x.x
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,15 +31,16 @@ | |
|
||
import java.security.SecureRandom; | ||
import java.util.*; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. please expand the * |
||
import java.util.concurrent.ConcurrentHashMap; | ||
dizzzz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** | ||
* A persistent login feature ("remember me") similar to the implementation in <a href="https://github.com/SpringSource/spring-security">Spring Security</a>, | ||
* which is based on <a href="http://jaspan.com/improved_persistent_login_cookie_best_practice">Improved Persistent Login Cookie | ||
* Best Practice</a> . | ||
* | ||
* <p> | ||
* The one-time tokens generated by this class are purely random and do not contain a user name or other information. For security reasons, | ||
* tokens and user information are not stored anywhere, so if the database is shut down, registered tokens will be gone. | ||
* | ||
* <p> | ||
* The one-time token approach has the negative effect that requests need to be made in sequence, which is sometimes difficult if an app uses | ||
* concurrent AJAX requests. Unfortunately, this is the price we have to pay for a sufficiently secure protection against | ||
* cookie stealing attacks. | ||
|
@@ -60,11 +61,11 @@ | |
|
||
public final static int DEFAULT_TOKEN_LENGTH = 16; | ||
|
||
public final static int INVALIDATION_TIMEOUT = 20000; | ||
public final static int INVALIDATION_TIMEOUT = 10000; | ||
Check notice on line 64 in extensions/modules/persistentlogin/src/main/java/org/exist/xquery/modules/persistentlogin/PersistentLogin.java
|
||
|
||
private Map<String, LoginDetails> seriesMap = Collections.synchronizedMap(new HashMap<>()); | ||
private final Map<String, LoginDetails> seriesMap = new ConcurrentHashMap<>(); | ||
Check notice on line 66 in extensions/modules/persistentlogin/src/main/java/org/exist/xquery/modules/persistentlogin/PersistentLogin.java
|
||
|
||
private SecureRandom random; | ||
private final SecureRandom random; | ||
Check notice on line 68 in extensions/modules/persistentlogin/src/main/java/org/exist/xquery/modules/persistentlogin/PersistentLogin.java
|
||
|
||
public PersistentLogin() { | ||
random = new SecureRandom(); | ||
|
@@ -73,10 +74,10 @@ | |
/** | ||
* Register the user and generate a first login token which will be valid for the next | ||
* call to {@link #lookup(String)}. | ||
* | ||
* <p> | ||
* The generated token will have the format base64(series-hash):base64(token-hash). | ||
* | ||
* @param user the user name | ||
* @param user the username | ||
* @param password the password | ||
* @param timeToLive timeout of the token | ||
* @return a first login token | ||
|
@@ -154,17 +155,18 @@ | |
|
||
public class LoginDetails { | ||
|
||
private String userName; | ||
private String password; | ||
private String token; | ||
private String series; | ||
private long expires; | ||
private DurationValue timeToLive; | ||
|
||
private final String userName; | ||
private final String password; | ||
private final String series; | ||
private final long expires; | ||
private final DurationValue timeToLive; | ||
|
||
// disable sequential token checking by default | ||
private boolean seqBehavior = false; | ||
private final boolean seqBehavior = false; | ||
|
||
private Map<String, Long> invalidatedTokens = new HashMap<>(); | ||
private final Map<String, Long> invalidatedTokens = new HashMap<>(); | ||
|
||
public LoginDetails(String user, String password, DurationValue timeToLive, long expires) { | ||
this.userName = user; | ||
|
@@ -176,19 +178,19 @@ | |
} | ||
|
||
public String getToken() { | ||
return this.token; | ||
return token; | ||
} | ||
|
||
public String getSeries() { | ||
return this.series; | ||
return series; | ||
} | ||
|
||
public String getUser() { | ||
return this.userName; | ||
return userName; | ||
} | ||
|
||
public String getPassword() { | ||
return this.password; | ||
return password; | ||
} | ||
|
||
public DurationValue getTimeToLive() { | ||
|
@@ -197,13 +199,15 @@ | |
|
||
public boolean checkAndUpdateToken(String token) { | ||
if (this.token.equals(token)) { | ||
timeoutCheck(); | ||
dizzzz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
update(); | ||
return true; | ||
} | ||
// check map of invalidating tokens | ||
Long timeout = invalidatedTokens.get(token); | ||
if (timeout == null) | ||
final Long timeout = invalidatedTokens.get(token); | ||
if (timeout == null) { | ||
return false; | ||
} | ||
// timed out: remove | ||
if (System.currentTimeMillis() > timeout) { | ||
invalidatedTokens.remove(token); | ||
|
@@ -213,23 +217,21 @@ | |
return true; | ||
} | ||
|
||
public String update() { | ||
timeoutCheck(); | ||
// leave a small time window until previous token is deleted | ||
public void update() { | ||
// leave a small time-window until previous token is deleted | ||
// to allow for concurrent requests | ||
invalidatedTokens.put(this.token, System.currentTimeMillis() + INVALIDATION_TIMEOUT); | ||
this.token = generateToken(); | ||
return this.token; | ||
invalidatedTokens.put(token, System.currentTimeMillis() + INVALIDATION_TIMEOUT); | ||
token = generateToken(); | ||
} | ||
|
||
private void timeoutCheck() { | ||
long now = System.currentTimeMillis(); | ||
final long now = System.currentTimeMillis(); | ||
invalidatedTokens.entrySet().removeIf(entry -> entry.getValue() < now); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return this.series + ":" + this.token; | ||
return series + ":" + token; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be replaced by a list of package imports from java.util?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeas please