OAuthTokenCache: Use account id as key

Current implementation assumes that OAuth provider always exposes
user name by using it for key in OAuth token cache. For some OAuth
providers (most notably Google OAuth provider) this is not the case.

Move from using user name to account id for token cache key. Postpone
populating the cache to the point when the authentication already took
place and use returned account id as cache key.

Bug: Issue 4627
Change-Id: I59f15b7c5ca8be6d52b59d21fac58cba88ba7fe3
This commit is contained in:
David Ostrovsky
2016-09-27 08:23:47 +02:00
parent 6147f6d17b
commit d5b21545d7
3 changed files with 28 additions and 41 deletions

View File

@@ -62,6 +62,7 @@ class OAuthSession {
private final OAuthTokenCache tokenCache;
private OAuthServiceProvider serviceProvider;
private OAuthUserInfo user;
private Account.Id accountId;
private String redirectToken;
private boolean linkMode;
@@ -80,7 +81,7 @@ class OAuthSession {
}
boolean isLoggedIn() {
return tokenCache.has(user);
return user != null;
}
boolean isOAuthFinal(HttpServletRequest request) {
@@ -101,13 +102,10 @@ class OAuthSession {
OAuthToken token = oauth.getAccessToken(
new OAuthVerifier(request.getParameter("code")));
user = oauth.getUserInfo(token);
if (user != null && token != null) {
tokenCache.put(user, token);
}
if (isLoggedIn()) {
log.debug("Login-SUCCESS " + this);
authenticateAndRedirect(request, response);
authenticateAndRedirect(request, response, token);
return true;
}
response.sendError(SC_UNAUTHORIZED);
@@ -128,7 +126,7 @@ class OAuthSession {
}
private void authenticateAndRedirect(HttpServletRequest req,
HttpServletResponse rsp) throws IOException {
HttpServletResponse rsp, OAuthToken token) throws IOException {
AuthRequest areq = new AuthRequest(user.getExternalId());
AuthResult arsp;
try {
@@ -147,6 +145,9 @@ class OAuthSession {
areq.setEmailAddress(user.getEmailAddress());
areq.setDisplayName(user.getDisplayName());
arsp = accountManager.authenticate(areq);
accountId = arsp.getAccountId();
tokenCache.put(accountId, token);
} catch (AccountException e) {
log.error("Unable to authenticate user \"" + user + "\"", e);
rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
@@ -215,7 +216,10 @@ class OAuthSession {
}
void logout() {
tokenCache.remove(user);
if (accountId != null) {
tokenCache.remove(accountId);
accountId = null;
}
user = null;
redirectToken = null;
serviceProvider = null;
@@ -247,8 +251,8 @@ class OAuthSession {
@Override
public String toString() {
return "OAuthSession [token=" + tokenCache.get(user) + ", user=" + user
+ "]";
return "OAuthSession [token=" + tokenCache.get(accountId) + ", user="
+ user + "]";
}
public void setServiceProvider(OAuthServiceProvider provider) {

View File

@@ -18,6 +18,7 @@ import com.google.gerrit.extensions.auth.oauth.OAuthToken;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.extensions.restapi.ResourceNotFoundException;
import com.google.gerrit.extensions.restapi.RestReadView;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.auth.oauth.OAuthTokenCache;
import com.google.gerrit.server.config.CanonicalWebUrl;
@@ -52,16 +53,13 @@ class GetOAuthToken implements RestReadView<AccountResource>{
if (self.get() != rsrc.getUser()) {
throw new AuthException("not allowed to get access token");
}
String username = rsrc.getUser().getAccount().getUserName();
if (username == null) {
throw new ResourceNotFoundException();
}
OAuthToken accessToken = tokenCache.get(username);
Account a = rsrc.getUser().getAccount();
OAuthToken accessToken = tokenCache.get(a.getId());
if (accessToken == null) {
throw new ResourceNotFoundException();
}
OAuthTokenInfo accessTokenInfo = new OAuthTokenInfo();
accessTokenInfo.username = username;
accessTokenInfo.username = a.getUserName();
accessTokenInfo.resourceHost = hostName;
accessTokenInfo.accessToken = accessToken.getToken();
accessTokenInfo.providerId = accessToken.getProviderId();

View File

@@ -19,8 +19,8 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.cache.Cache;
import com.google.gerrit.extensions.auth.oauth.OAuthToken;
import com.google.gerrit.extensions.auth.oauth.OAuthTokenEncrypter;
import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.cache.CacheModule;
import com.google.inject.Inject;
import com.google.inject.Module;
@@ -37,54 +37,39 @@ public class OAuthTokenCache {
return new CacheModule() {
@Override
protected void configure() {
persist(OAUTH_TOKENS, String.class, OAuthToken.class);
persist(OAUTH_TOKENS, Account.Id.class, OAuthToken.class);
}
};
}
private final Cache<String, OAuthToken> cache;
private final Cache<Account.Id, OAuthToken> cache;
@Inject
OAuthTokenCache(@Named(OAUTH_TOKENS) Cache<String, OAuthToken> cache,
OAuthTokenCache(@Named(OAUTH_TOKENS) Cache<Account.Id, OAuthToken> cache,
DynamicItem<OAuthTokenEncrypter> encrypter) {
this.cache = cache;
this.encrypter = encrypter;
}
public boolean has(OAuthUserInfo user) {
return user != null
? cache.getIfPresent(user.getUserName()) != null
: false;
}
public OAuthToken get(OAuthUserInfo user) {
return user != null
? get(user.getUserName())
: null;
}
public OAuthToken get(String userName) {
OAuthToken accessToken = cache.getIfPresent(userName);
public OAuthToken get(Account.Id id) {
OAuthToken accessToken = cache.getIfPresent(id);
if (accessToken == null) {
return null;
}
accessToken = decrypt(accessToken);
if (accessToken.isExpired()) {
cache.invalidate(userName);
cache.invalidate(id);
return null;
}
return accessToken;
}
public void put(OAuthUserInfo user, OAuthToken accessToken) {
cache.put(checkNotNull(user.getUserName()),
encrypt(checkNotNull(accessToken)));
public void put(Account.Id id, OAuthToken accessToken) {
cache.put(id, encrypt(checkNotNull(accessToken)));
}
public void remove(OAuthUserInfo user) {
if (user != null) {
cache.invalidate(user.getUserName());
}
public void remove(Account.Id id) {
cache.invalidate(id);
}
private OAuthToken encrypt(OAuthToken token) {