diff options
Diffstat (limited to 'gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java')
-rw-r--r-- | gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java | 54 |
1 files changed, 44 insertions, 10 deletions
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java index 43c99adc8e..55d0ca58df 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/WebSessionManager.java @@ -14,6 +14,7 @@ package com.google.gerrit.httpd; +import static com.google.gerrit.httpd.CacheBasedWebSession.MAX_AGE_MINUTES; import static com.google.gerrit.server.ioutil.BasicSerialization.readFixInt64; import static com.google.gerrit.server.ioutil.BasicSerialization.readString; import static com.google.gerrit.server.ioutil.BasicSerialization.readVarInt32; @@ -23,15 +24,19 @@ import static com.google.gerrit.server.ioutil.BasicSerialization.writeString; import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32; import static java.util.concurrent.TimeUnit.HOURS; import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static java.util.concurrent.TimeUnit.SECONDS; +import static java.util.concurrent.TimeUnit.MINUTES; -import com.google.gerrit.reviewdb.Account; -import com.google.gerrit.reviewdb.AccountExternalId; +import com.google.gerrit.reviewdb.client.Account; +import com.google.gerrit.reviewdb.client.AccountExternalId; import com.google.gerrit.server.cache.Cache; +import com.google.gerrit.server.config.ConfigUtil; +import com.google.gerrit.server.config.GerritServerConfig; import com.google.inject.Inject; import com.google.inject.Singleton; import com.google.inject.name.Named; +import org.eclipse.jgit.lib.Config; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; @@ -47,13 +52,19 @@ class WebSessionManager { return System.currentTimeMillis(); } + private final long sessionMaxAgeMillis; private final SecureRandom prng; private final Cache<Key, Val> self; @Inject - WebSessionManager(@Named(CACHE_NAME) final Cache<Key, Val> cache) { + WebSessionManager(@GerritServerConfig Config cfg, + @Named(CACHE_NAME) final Cache<Key, Val> cache) { prng = new SecureRandom(); self = cache; + + sessionMaxAgeMillis = MINUTES.toMillis(ConfigUtil.getTimeUnit(cfg, + "cache", CACHE_NAME, "maxAge", + MAX_AGE_MINUTES, MINUTES)); } Key createKey(final Account.Id who) { @@ -78,23 +89,33 @@ class WebSessionManager { final Account.Id who = val.getAccountId(); final boolean remember = val.isPersistentCookie(); final AccountExternalId.Key lastLogin = val.getExternalId(); + final String xsrfToken = val.getXsrfToken(); - return createVal(key, who, remember, lastLogin); + return createVal(key, who, remember, lastLogin, xsrfToken); } Val createVal(final Key key, final Account.Id who, final boolean remember, - final AccountExternalId.Key lastLogin) { + final AccountExternalId.Key lastLogin, String xsrfToken) { // Refresh the cookie every hour or when it is half-expired. // This reduces the odds that the user session will be kicked // early but also avoids us needing to refresh the cookie on // every single request. // - final long halfAgeRefresh = self.getTimeToLive(MILLISECONDS) >>> 1; + final long halfAgeRefresh = sessionMaxAgeMillis >>> 1; final long minRefresh = MILLISECONDS.convert(1, HOURS); final long refresh = Math.min(halfAgeRefresh, minRefresh); final long refreshCookieAt = now() + refresh; - final Val val = new Val(who, refreshCookieAt, remember, lastLogin); + if (xsrfToken == null) { + // If we don't yet have a token for this session, establish one. + // + final int nonceLen = 20; + final byte[] rnd = new byte[nonceLen]; + prng.nextBytes(rnd); + xsrfToken = CookieBase64.encode(rnd); + } + + Val val = new Val(who, refreshCookieAt, remember, lastLogin, xsrfToken); self.put(key, val); return val; } @@ -104,7 +125,7 @@ class WebSessionManager { // Client may store the cookie until we would remove it from our // own cache, after which it will certainly be invalid. // - return (int) self.getTimeToLive(SECONDS); + return (int) MILLISECONDS.toSeconds(sessionMaxAgeMillis); } else { // Client should not store the cookie, as the user asked for us // to not remember them long-term. Sending -1 as the age will @@ -162,13 +183,16 @@ class WebSessionManager { private transient long refreshCookieAt; private transient boolean persistentCookie; private transient AccountExternalId.Key externalId; + private transient String xsrfToken; Val(final Account.Id accountId, final long refreshCookieAt, - final boolean persistentCookie, final AccountExternalId.Key externalId) { + final boolean persistentCookie, final AccountExternalId.Key externalId, + final String xsrfToken) { this.accountId = accountId; this.refreshCookieAt = refreshCookieAt; this.persistentCookie = persistentCookie; this.externalId = externalId; + this.xsrfToken = xsrfToken; } Account.Id getAccountId() { @@ -187,6 +211,10 @@ class WebSessionManager { return persistentCookie; } + String getXsrfToken() { + return xsrfToken; + } + private void writeObject(final ObjectOutputStream out) throws IOException { writeVarInt32(out, 1); writeVarInt32(out, accountId.get()); @@ -202,6 +230,9 @@ class WebSessionManager { writeString(out, externalId.get()); } + writeVarInt32(out, 5); + writeString(out, xsrfToken); + writeVarInt32(out, 0); } @@ -223,6 +254,9 @@ class WebSessionManager { case 4: externalId = new AccountExternalId.Key(readString(in)); continue; + case 5: + xsrfToken = readString(in); + continue; default: throw new IOException("Unknown tag found in object: " + tag); } |