blob: 5b62f962165894af4f07a5b0bc008c4ed21a1fac [file] [log] [blame]
// Copyright (C) 2009 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.gerrit.httpd;
import static java.util.concurrent.TimeUnit.HOURS;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Strings;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.common.UsedAt;
import com.google.gerrit.entities.Account;
import com.google.gerrit.extensions.restapi.BadRequestException;
import com.google.gerrit.httpd.restapi.ParameterParser;
import com.google.gerrit.server.AccessPath;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.PropertyMap;
import com.google.gerrit.server.account.AccountCache;
import com.google.gerrit.server.account.AuthResult;
import com.google.gerrit.server.account.externalids.ExternalId;
import com.google.gerrit.server.config.AuthConfig;
import com.google.inject.Provider;
import java.util.EnumSet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jgit.http.server.GitSmartHttpTools;
public abstract class CacheBasedWebSession extends WebSession {
@VisibleForTesting public static final String ACCOUNT_COOKIE = "GerritAccount";
@UsedAt(UsedAt.Project.PLUGIN_WEBSESSION_FLATFILE)
public static final long MAX_AGE_MINUTES = HOURS.toMinutes(12);
private final HttpServletRequest request;
private final HttpServletResponse response;
private final WebSessionManager manager;
private final AuthConfig authConfig;
private final Provider<AnonymousUser> anonymousProvider;
private final IdentifiedUser.RequestFactory identified;
private final EnumSet<AccessPath> okPaths = EnumSet.of(AccessPath.UNKNOWN);
private final AccountCache byIdCache;
private Cookie outCookie;
private WebSessionManager.Key key;
private WebSessionManager.Val val;
private CurrentUser user;
protected CacheBasedWebSession(
HttpServletRequest request,
HttpServletResponse response,
WebSessionManager manager,
AuthConfig authConfig,
Provider<AnonymousUser> anonymousProvider,
IdentifiedUser.RequestFactory identified,
AccountCache byIdCache) {
this.request = request;
this.response = response;
this.manager = manager;
this.authConfig = authConfig;
this.anonymousProvider = anonymousProvider;
this.identified = identified;
this.byIdCache = byIdCache;
String cookie = readCookie(request);
if (cookie != null) {
authFromCookie(cookie);
} else if (request.getRequestURI() == null || !GitSmartHttpTools.isGitClient(request)) {
String token;
try {
token = ParameterParser.getQueryParams(request).accessToken();
} catch (BadRequestException e) {
token = null;
}
if (token != null) {
authFromQueryParameter(token);
}
}
if (val != null && !checkAccountStatus(val.getAccountId())) {
val = null;
okPaths.clear();
}
if (val != null && val.needsCookieRefresh()) {
// Session is more than half old; update cache entry with new expiration date.
val = manager.createVal(key, val);
}
}
private void authFromCookie(String cookie) {
key = new WebSessionManager.Key(cookie);
val = manager.get(key);
String token = request.getHeader(XsrfConstants.XSRF_HEADER_NAME);
if (val != null && token != null && token.equals(val.getAuth())) {
okPaths.add(AccessPath.REST_API);
}
}
private void authFromQueryParameter(String accessToken) {
key = new WebSessionManager.Key(accessToken);
val = manager.get(key);
if (val != null) {
okPaths.add(AccessPath.REST_API);
}
}
private static String readCookie(HttpServletRequest request) {
Cookie[] all = request.getCookies();
if (all != null) {
for (Cookie c : all) {
if (ACCOUNT_COOKIE.equals(c.getName())) {
return Strings.emptyToNull(c.getValue());
}
}
}
return null;
}
@Override
public boolean isSignedIn() {
return val != null;
}
@Override
@Nullable
public String getXGerritAuth() {
return isSignedIn() ? val.getAuth() : null;
}
@Override
public boolean isValidXGerritAuth(String keyIn) {
return keyIn.equals(getXGerritAuth());
}
@Override
public boolean isAccessPathOk(AccessPath path) {
return okPaths.contains(path);
}
@Override
public void setAccessPathOk(AccessPath path, boolean ok) {
if (ok) {
okPaths.add(path);
} else {
okPaths.remove(path);
}
}
@Override
public CurrentUser getUser() {
if (user == null) {
if (isSignedIn()) {
user = identified.create(val.getAccountId(), getUserProperties(val));
} else {
user = anonymousProvider.get();
}
}
return user;
}
private static PropertyMap getUserProperties(@Nullable WebSessionManager.Val val) {
if (val == null || val.getExternalId() == null) {
return PropertyMap.EMPTY;
}
return PropertyMap.builder()
.put(CurrentUser.LAST_LOGIN_EXTERNAL_ID_PROPERTY_KEY, val.getExternalId())
.build();
}
@Override
public void login(AuthResult res, boolean rememberMe) {
Account.Id id = res.getAccountId();
ExternalId.Key identity = res.getExternalId();
if (val != null) {
manager.destroy(key);
}
if (!checkAccountStatus(id)) {
val = null;
return;
}
key = manager.createKey(id);
val = manager.createVal(key, id, rememberMe, identity, null, null);
saveCookie();
user = identified.create(val.getAccountId(), getUserProperties(val));
}
/** Set the user account for this current request only. */
@Override
public void setUserAccountId(Account.Id id) {
key = new WebSessionManager.Key("id:" + id);
val = new WebSessionManager.Val(id, 0, false, null, 0, null, null);
user = identified.runAs(id, user, PropertyMap.EMPTY);
}
@Override
public void logout() {
if (val != null) {
manager.destroy(key);
key = null;
val = null;
saveCookie();
user = anonymousProvider.get();
}
}
@Override
public String getSessionId() {
return val != null ? val.getSessionId() : null;
}
private boolean checkAccountStatus(Account.Id id) {
return byIdCache.get(id).filter(as -> as.account().isActive()).isPresent();
}
private void saveCookie() {
if (response == null) {
return;
}
final String token;
final int ageSeconds;
if (key == null) {
token = "";
ageSeconds = 0 /* erase at client */;
} else {
token = key.getToken();
ageSeconds = manager.getCookieAge(val);
}
String path = authConfig.getCookiePath();
if (Strings.isNullOrEmpty(path)) {
path = request.getContextPath();
if (Strings.isNullOrEmpty(path)) {
path = "/";
}
}
if (outCookie != null) {
throw new IllegalStateException("Cookie " + ACCOUNT_COOKIE + " was set");
}
outCookie = new Cookie(ACCOUNT_COOKIE, token);
String domain = authConfig.getCookieDomain();
if (!Strings.isNullOrEmpty(domain)) {
outCookie.setDomain(domain);
}
outCookie.setSecure(isSecure(request));
outCookie.setPath(path);
outCookie.setMaxAge(ageSeconds);
outCookie.setSecure(authConfig.getCookieSecure());
response.addCookie(outCookie);
}
private static boolean isSecure(HttpServletRequest req) {
return req.isSecure() || "https".equals(req.getScheme());
}
}