AccountCache#getByUsername: Return Optional<AccountState>

This makes it more explicit that callers must handle the case where the
returned AccountState is absent.

Change-Id: Ib679cdd6d05f712c0d0c8780eb607780a4a0a79d
Signed-off-by: Edwin Kempin <ekempin@google.com>
This commit is contained in:
Edwin Kempin
2018-01-18 14:31:57 +01:00
parent 2a6644f5b1
commit de8ff6ae8f
10 changed files with 53 additions and 41 deletions

View File

@@ -31,6 +31,7 @@ import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import java.io.IOException; import java.io.IOException;
import java.util.Locale; import java.util.Locale;
import java.util.Optional;
import javax.servlet.Filter; import javax.servlet.Filter;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.FilterConfig; import javax.servlet.FilterConfig;
@@ -98,13 +99,14 @@ class ContainerAuthFilter implements Filter {
if (config.getBoolean("auth", "userNameToLowerCase", false)) { if (config.getBoolean("auth", "userNameToLowerCase", false)) {
username = username.toLowerCase(Locale.US); username = username.toLowerCase(Locale.US);
} }
final AccountState who = accountCache.getByUsername(username); Optional<AccountState> who =
if (who == null || !who.getAccount().isActive()) { accountCache.getByUsername(username).filter(a -> a.getAccount().isActive());
if (!who.isPresent()) {
rsp.sendError(SC_UNAUTHORIZED); rsp.sendError(SC_UNAUTHORIZED);
return false; return false;
} }
WebSession ws = session.get(); WebSession ws = session.get();
ws.setUserAccountId(who.getAccount().getId()); ws.setUserAccountId(who.get().getAccount().getId());
ws.setAccessPathOk(AccessPath.GIT, true); ws.setAccessPathOk(AccessPath.GIT, true);
ws.setAccessPathOk(AccessPath.REST_API, true); ws.setAccessPathOk(AccessPath.REST_API, true);
return true; return true;

View File

@@ -36,6 +36,7 @@ import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import java.io.IOException; import java.io.IOException;
import java.util.Locale; import java.util.Locale;
import java.util.Optional;
import javax.servlet.Filter; import javax.servlet.Filter;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.FilterConfig; import javax.servlet.FilterConfig;
@@ -127,8 +128,9 @@ class ProjectBasicAuthFilter implements Filter {
username = username.toLowerCase(Locale.US); username = username.toLowerCase(Locale.US);
} }
final AccountState who = accountCache.getByUsername(username); Optional<AccountState> accountState =
if (who == null || !who.getAccount().isActive()) { accountCache.getByUsername(username).filter(a -> a.getAccount().isActive());
if (!accountState.isPresent()) {
log.warn( log.warn(
"Authentication failed for " "Authentication failed for "
+ username + username
@@ -137,6 +139,7 @@ class ProjectBasicAuthFilter implements Filter {
return false; return false;
} }
AccountState who = accountState.get();
GitBasicAuthPolicy gitBasicAuthPolicy = authConfig.getGitBasicAuthPolicy(); GitBasicAuthPolicy gitBasicAuthPolicy = authConfig.getGitBasicAuthPolicy();
if (gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP if (gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP
|| gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP_LDAP) { || gitBasicAuthPolicy == GitBasicAuthPolicy.HTTP_LDAP) {
@@ -157,7 +160,7 @@ class ProjectBasicAuthFilter implements Filter {
setUserIdentified(whoAuthResult.getAccountId()); setUserIdentified(whoAuthResult.getAccountId());
return true; return true;
} catch (NoSuchUserException e) { } catch (NoSuchUserException e) {
if (who.checkPassword(password, who.getUserName())) { if (who.checkPassword(password, username)) {
return succeedAuthentication(who); return succeedAuthentication(who);
} }
log.warn(authenticationFailedMsg(username, req), e); log.warn(authenticationFailedMsg(username, req), e);

View File

@@ -25,6 +25,7 @@ import com.google.gerrit.extensions.auth.oauth.OAuthLoginProvider;
import com.google.gerrit.extensions.registration.DynamicItem; import com.google.gerrit.extensions.registration.DynamicItem;
import com.google.gerrit.extensions.registration.DynamicMap; import com.google.gerrit.extensions.registration.DynamicMap;
import com.google.gerrit.extensions.registration.DynamicMap.Entry; import com.google.gerrit.extensions.registration.DynamicMap.Entry;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.AccessPath; import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.account.AccountCache; import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AccountException; import com.google.gerrit.server.account.AccountException;
@@ -40,6 +41,7 @@ import java.io.UnsupportedEncodingException;
import java.net.URLDecoder; import java.net.URLDecoder;
import java.util.Locale; import java.util.Locale;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.Optional;
import javax.servlet.Filter; import javax.servlet.Filter;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.FilterConfig; import javax.servlet.FilterConfig;
@@ -151,8 +153,9 @@ class ProjectOAuthFilter implements Filter {
return false; return false;
} }
AccountState who = accountCache.getByUsername(authInfo.username); Optional<AccountState> who =
if (who == null || !who.getAccount().isActive()) { accountCache.getByUsername(authInfo.username).filter(a -> a.getAccount().isActive());
if (!who.isPresent()) {
log.warn( log.warn(
authenticationFailedMsg(authInfo.username, req) authenticationFailedMsg(authInfo.username, req)
+ ": account inactive or not provisioned in Gerrit"); + ": account inactive or not provisioned in Gerrit");
@@ -160,9 +163,10 @@ class ProjectOAuthFilter implements Filter {
return false; return false;
} }
Account account = who.get().getAccount();
AuthRequest authRequest = AuthRequest.forExternalUser(authInfo.username); AuthRequest authRequest = AuthRequest.forExternalUser(authInfo.username);
authRequest.setEmailAddress(who.getAccount().getPreferredEmail()); authRequest.setEmailAddress(account.getPreferredEmail());
authRequest.setDisplayName(who.getAccount().getFullName()); authRequest.setDisplayName(account.getFullName());
authRequest.setPassword(authInfo.tokenOrSecret); authRequest.setPassword(authInfo.tokenOrSecret);
authRequest.setAuthPlugin(authInfo.pluginName); authRequest.setAuthPlugin(authInfo.pluginName);
authRequest.setAuthProvider(authInfo.exportName); authRequest.setAuthProvider(authInfo.exportName);

View File

@@ -17,6 +17,7 @@ package com.google.gerrit.server.account;
import com.google.gerrit.common.Nullable; import com.google.gerrit.common.Nullable;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import java.io.IOException; import java.io.IOException;
import java.util.Optional;
/** Caches important (but small) account state to avoid database hits. */ /** Caches important (but small) account state to avoid database hits. */
public interface AccountCache { public interface AccountCache {
@@ -50,10 +51,9 @@ public interface AccountCache {
* *
* @param username username of the account that should be retrieved * @param username username of the account that should be retrieved
* @return {@code AccountState} instance for the given username, if no account with this username * @return {@code AccountState} instance for the given username, if no account with this username
* exists or if loading the external ID fails {@code null} is returned * exists or if loading the external ID fails {@link Optional#empty()} is returned
*/ */
@Nullable Optional<AccountState> getByUsername(String username);
AccountState getByUsername(String username);
/** /**
* Evicts the account from the cache and triggers a reindex for it. * Evicts the account from the cache and triggers a reindex for it.

View File

@@ -98,14 +98,13 @@ public class AccountCacheImpl implements AccountCache {
} }
@Override @Override
@Nullable public Optional<AccountState> getByUsername(String username) {
public AccountState getByUsername(String username) {
try { try {
ExternalId extId = externalIds.get(ExternalId.Key.create(SCHEME_USERNAME, username)); ExternalId extId = externalIds.get(ExternalId.Key.create(SCHEME_USERNAME, username));
if (extId == null) { if (extId == null) {
return null; return Optional.empty();
} }
return getOrNull(extId.accountId()); return Optional.ofNullable(getOrNull(extId.accountId()));
} catch (IOException | ConfigInvalidException e) { } catch (IOException | ConfigInvalidException e) {
log.warn("Cannot load AccountState for username " + username, e); log.warn("Cannot load AccountState for username " + username, e);
return null; return null;

View File

@@ -17,6 +17,7 @@ package com.google.gerrit.server.account;
import static com.google.common.collect.ImmutableSet.toImmutableSet; import static com.google.common.collect.ImmutableSet.toImmutableSet;
import static java.util.stream.Collectors.toSet; import static java.util.stream.Collectors.toSet;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Streams; import com.google.common.collect.Streams;
import com.google.gerrit.reviewdb.client.Account; import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.server.query.account.InternalAccountQuery; import com.google.gerrit.server.query.account.InternalAccountQuery;
@@ -28,6 +29,7 @@ import java.io.IOException;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@@ -106,9 +108,9 @@ public class AccountResolver {
} }
if (nameOrEmail.matches(Account.USER_NAME_PATTERN)) { if (nameOrEmail.matches(Account.USER_NAME_PATTERN)) {
AccountState who = byId.getByUsername(nameOrEmail); Optional<AccountState> who = byId.getByUsername(nameOrEmail);
if (who != null) { if (who.isPresent()) {
return Collections.singleton(who.getAccount().getId()); return ImmutableSet.of(who.map(a -> a.getAccount().getId()).get());
} }
} }

View File

@@ -54,10 +54,10 @@ public class InternalAuthBackend implements AuthBackend {
username = req.getUsername(); username = req.getUsername();
} }
final AccountState who = accountCache.getByUsername(username); AccountState who =
if (who == null) { accountCache.getByUsername(username).orElseThrow(() -> new UnknownUserException());
throw new UnknownUserException();
} else if (!who.getAccount().isActive()) { if (!who.getAccount().isActive()) {
throw new UserNotAllowedException( throw new UserNotAllowedException(
"Authentication failed for " "Authentication failed for "
+ username + username

View File

@@ -23,6 +23,7 @@ import com.google.gerrit.server.config.GerritServerConfig;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import java.util.Locale; import java.util.Locale;
import java.util.Optional;
import org.apache.sshd.server.auth.gss.GSSAuthenticator; import org.apache.sshd.server.auth.gss.GSSAuthenticator;
import org.apache.sshd.server.session.ServerSession; import org.apache.sshd.server.session.ServerSession;
import org.eclipse.jgit.lib.Config; import org.eclipse.jgit.lib.Config;
@@ -52,7 +53,7 @@ class GerritGSSAuthenticator extends GSSAuthenticator {
@Override @Override
public boolean validateIdentity(ServerSession session, String identity) { public boolean validateIdentity(ServerSession session, String identity) {
final SshSession sd = session.getAttribute(SshSession.KEY); SshSession sd = session.getAttribute(SshSession.KEY);
int at = identity.indexOf('@'); int at = identity.indexOf('@');
String username; String username;
if (at == -1) { if (at == -1) {
@@ -63,18 +64,19 @@ class GerritGSSAuthenticator extends GSSAuthenticator {
if (config.getBoolean("auth", "userNameToLowerCase", false)) { if (config.getBoolean("auth", "userNameToLowerCase", false)) {
username = username.toLowerCase(Locale.US); username = username.toLowerCase(Locale.US);
} }
AccountState state = accounts.getByUsername(username);
Account account = state == null ? null : state.getAccount(); Optional<Account> account =
boolean active = account != null && account.isActive(); accounts.getByUsername(username).map(AccountState::getAccount).filter(Account::isActive);
if (active) { if (!account.isPresent()) {
return SshUtil.success( return false;
username,
session,
sshScope,
sshLog,
sd,
SshUtil.createUser(sd, userFactory, account.getId()));
} }
return false;
return SshUtil.success(
username,
session,
sshScope,
sshLog,
sd,
SshUtil.createUser(sd, userFactory, account.get().getId()));
} }
} }

View File

@@ -23,6 +23,7 @@ import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.AllUsersNameProvider; import com.google.gerrit.server.config.AllUsersNameProvider;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional;
/** Fake implementation of {@link AccountCache} for testing. */ /** Fake implementation of {@link AccountCache} for testing. */
public class FakeAccountCache implements AccountCache { public class FakeAccountCache implements AccountCache {
@@ -50,9 +51,8 @@ public class FakeAccountCache implements AccountCache {
} }
@Override @Override
@Nullable public synchronized Optional<AccountState> getByUsername(String username) {
public synchronized AccountState getByUsername(String username) { return Optional.ofNullable(byUsername.get(username));
return byUsername.get(username);
} }
@Override @Override