Remove AuthMethod and add access token to RestApi
Authentication for API calls will be handled using an access token in the "Authorization: OAuth access_token" style. Browsers do not use this when making requests unless they use an XmlHttpRequest. If the value used as the access_token is not available cross-site then the API call cannot be made by hijacking attempts. Change-Id: I33654bcaa247cb95a57b03d2df112ca95e970185
This commit is contained in:
@@ -23,7 +23,7 @@ import java.util.List;
|
|||||||
public class HostPageData {
|
public class HostPageData {
|
||||||
public Account account;
|
public Account account;
|
||||||
public AccountDiffPreference accountDiffPref;
|
public AccountDiffPreference accountDiffPref;
|
||||||
public String xsrfToken;
|
public String accessToken;
|
||||||
public GerritConfig config;
|
public GerritConfig config;
|
||||||
public Theme theme;
|
public Theme theme;
|
||||||
public List<String> plugins;
|
public List<String> plugins;
|
||||||
|
@@ -93,7 +93,7 @@ public class Gerrit implements EntryPoint {
|
|||||||
private static HostPageData.Theme myTheme;
|
private static HostPageData.Theme myTheme;
|
||||||
private static Account myAccount;
|
private static Account myAccount;
|
||||||
private static AccountDiffPreference myAccountDiffPref;
|
private static AccountDiffPreference myAccountDiffPref;
|
||||||
private static String xsrfToken;
|
private static String accessToken;
|
||||||
|
|
||||||
private static MorphingTabPanel menuLeft;
|
private static MorphingTabPanel menuLeft;
|
||||||
private static LinkMenuBar menuRight;
|
private static LinkMenuBar menuRight;
|
||||||
@@ -239,6 +239,11 @@ public class Gerrit implements EntryPoint {
|
|||||||
return myAccount;
|
return myAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return access token to prove user identity during REST API calls. */
|
||||||
|
public static String getAccessToken() {
|
||||||
|
return accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
/** @return the currently signed in users's diff preferences; null if no diff preferences defined for the account */
|
/** @return the currently signed in users's diff preferences; null if no diff preferences defined for the account */
|
||||||
public static AccountDiffPreference getAccountDiffPreference() {
|
public static AccountDiffPreference getAccountDiffPreference() {
|
||||||
return myAccountDiffPref;
|
return myAccountDiffPref;
|
||||||
@@ -333,7 +338,7 @@ public class Gerrit implements EntryPoint {
|
|||||||
static void deleteSessionCookie() {
|
static void deleteSessionCookie() {
|
||||||
myAccount = null;
|
myAccount = null;
|
||||||
myAccountDiffPref = null;
|
myAccountDiffPref = null;
|
||||||
xsrfToken = null;
|
accessToken = null;
|
||||||
refreshMenuBar();
|
refreshMenuBar();
|
||||||
|
|
||||||
// If the cookie was HttpOnly, this request to delete it will
|
// If the cookie was HttpOnly, this request to delete it will
|
||||||
@@ -383,7 +388,7 @@ public class Gerrit implements EntryPoint {
|
|||||||
myTheme = result.theme;
|
myTheme = result.theme;
|
||||||
if (result.account != null) {
|
if (result.account != null) {
|
||||||
myAccount = result.account;
|
myAccount = result.account;
|
||||||
xsrfToken = result.xsrfToken;
|
accessToken = result.accessToken;
|
||||||
}
|
}
|
||||||
if (result.accountDiffPref != null) {
|
if (result.accountDiffPref != null) {
|
||||||
myAccountDiffPref = result.accountDiffPref;
|
myAccountDiffPref = result.accountDiffPref;
|
||||||
@@ -530,7 +535,7 @@ public class Gerrit implements EntryPoint {
|
|||||||
JsonUtil.setDefaultXsrfManager(new XsrfManager() {
|
JsonUtil.setDefaultXsrfManager(new XsrfManager() {
|
||||||
@Override
|
@Override
|
||||||
public String getToken(JsonDefTarget proxy) {
|
public String getToken(JsonDefTarget proxy) {
|
||||||
return xsrfToken;
|
return accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
package com.google.gerrit.client.rpc;
|
package com.google.gerrit.client.rpc;
|
||||||
|
|
||||||
|
import com.google.gerrit.client.Gerrit;
|
||||||
import com.google.gerrit.client.RpcStatus;
|
import com.google.gerrit.client.RpcStatus;
|
||||||
import com.google.gwt.core.client.GWT;
|
import com.google.gwt.core.client.GWT;
|
||||||
import com.google.gwt.core.client.JavaScriptObject;
|
import com.google.gwt.core.client.JavaScriptObject;
|
||||||
@@ -161,6 +162,9 @@ public class RestApi {
|
|||||||
public <T extends JavaScriptObject> void send(final AsyncCallback<T> cb) {
|
public <T extends JavaScriptObject> void send(final AsyncCallback<T> cb) {
|
||||||
RequestBuilder req = new RequestBuilder(RequestBuilder.GET, url.toString());
|
RequestBuilder req = new RequestBuilder(RequestBuilder.GET, url.toString());
|
||||||
req.setHeader("Accept", JsonConstants.JSON_TYPE);
|
req.setHeader("Accept", JsonConstants.JSON_TYPE);
|
||||||
|
if (Gerrit.getAccessToken() != null) {
|
||||||
|
req.setHeader("Authorization", "OAuth " + Gerrit.getAccessToken());
|
||||||
|
}
|
||||||
req.setCallback(new MyRequestCallback<T>(cb));
|
req.setCallback(new MyRequestCallback<T>(cb));
|
||||||
try {
|
try {
|
||||||
RpcStatus.INSTANCE.onRpcStart();
|
RpcStatus.INSTANCE.onRpcStart();
|
||||||
|
@@ -25,7 +25,6 @@ import com.google.gerrit.server.AccessPath;
|
|||||||
import com.google.gerrit.server.AnonymousUser;
|
import com.google.gerrit.server.AnonymousUser;
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.account.AuthMethod;
|
|
||||||
import com.google.gerrit.server.account.AuthResult;
|
import com.google.gerrit.server.account.AuthResult;
|
||||||
import com.google.gerrit.server.cache.CacheModule;
|
import com.google.gerrit.server.cache.CacheModule;
|
||||||
import com.google.gerrit.server.config.AuthConfig;
|
import com.google.gerrit.server.config.AuthConfig;
|
||||||
@@ -34,6 +33,8 @@ import com.google.inject.Module;
|
|||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.servlet.RequestScoped;
|
import com.google.inject.servlet.RequestScoped;
|
||||||
|
|
||||||
|
import org.eclipse.jgit.http.server.GitSmartHttpTools;
|
||||||
|
|
||||||
import javax.servlet.http.Cookie;
|
import javax.servlet.http.Cookie;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
@@ -65,9 +66,8 @@ public final class CacheBasedWebSession implements WebSession {
|
|||||||
private final AuthConfig authConfig;
|
private final AuthConfig authConfig;
|
||||||
private final Provider<AnonymousUser> anonymousProvider;
|
private final Provider<AnonymousUser> anonymousProvider;
|
||||||
private final IdentifiedUser.RequestFactory identified;
|
private final IdentifiedUser.RequestFactory identified;
|
||||||
private AccessPath accessPath = AccessPath.WEB_UI;
|
private AccessPath accessPath;
|
||||||
private Cookie outCookie;
|
private Cookie outCookie;
|
||||||
private AuthMethod authMethod;
|
|
||||||
|
|
||||||
private Key key;
|
private Key key;
|
||||||
private Val val;
|
private Val val;
|
||||||
@@ -85,7 +85,17 @@ public final class CacheBasedWebSession implements WebSession {
|
|||||||
this.anonymousProvider = anonymousProvider;
|
this.anonymousProvider = anonymousProvider;
|
||||||
this.identified = identified;
|
this.identified = identified;
|
||||||
|
|
||||||
final String cookie = readCookie();
|
String cookie = request.getHeader("Authorization");
|
||||||
|
if (cookie != null && cookie.startsWith("OAuth ")) {
|
||||||
|
cookie = cookie.substring("OAuth ".length());
|
||||||
|
accessPath = AccessPath.REST_API;
|
||||||
|
} else if (cookie != null && GitSmartHttpTools.isGitClient(request)) {
|
||||||
|
accessPath = AccessPath.GIT;
|
||||||
|
} else {
|
||||||
|
cookie = readCookie();
|
||||||
|
accessPath = AccessPath.WEB_BROWSER;
|
||||||
|
}
|
||||||
|
|
||||||
if (cookie != null) {
|
if (cookie != null) {
|
||||||
key = new Key(cookie);
|
key = new Key(cookie);
|
||||||
val = manager.get(key);
|
val = manager.get(key);
|
||||||
@@ -93,7 +103,6 @@ public final class CacheBasedWebSession implements WebSession {
|
|||||||
key = null;
|
key = null;
|
||||||
val = null;
|
val = null;
|
||||||
}
|
}
|
||||||
authMethod = isSignedIn() ? AuthMethod.COOKIE : AuthMethod.NONE;
|
|
||||||
|
|
||||||
if (isSignedIn() && val.needsCookieRefresh()) {
|
if (isSignedIn() && val.needsCookieRefresh()) {
|
||||||
// Cookie is more than half old. Send the cookie again to the
|
// Cookie is more than half old. Send the cookie again to the
|
||||||
@@ -124,14 +133,8 @@ public final class CacheBasedWebSession implements WebSession {
|
|||||||
return val != null;
|
return val != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getToken() {
|
public String getAccessToken() {
|
||||||
return isSignedIn() ? val.getXsrfToken() : null;
|
return isSignedIn() ? key.getToken() : null;
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isTokenValid(final String inputToken) {
|
|
||||||
return isSignedIn() //
|
|
||||||
&& val.getXsrfToken() != null //
|
|
||||||
&& val.getXsrfToken().equals(inputToken);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountExternalId.Key getLastLoginExternalId() {
|
public AccountExternalId.Key getLastLoginExternalId() {
|
||||||
@@ -145,8 +148,7 @@ public final class CacheBasedWebSession implements WebSession {
|
|||||||
return anonymousProvider.get();
|
return anonymousProvider.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void login(final AuthResult res, final AuthMethod meth,
|
public void login(final AuthResult res, final boolean rememberMe) {
|
||||||
final boolean rememberMe) {
|
|
||||||
final Account.Id id = res.getAccountId();
|
final Account.Id id = res.getAccountId();
|
||||||
final AccountExternalId.Key identity = res.getExternalId();
|
final AccountExternalId.Key identity = res.getExternalId();
|
||||||
|
|
||||||
@@ -155,22 +157,14 @@ public final class CacheBasedWebSession implements WebSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
key = manager.createKey(id);
|
key = manager.createKey(id);
|
||||||
val = manager.createVal(key, id, rememberMe, identity, null);
|
val = manager.createVal(key, id, rememberMe, identity);
|
||||||
saveCookie();
|
saveCookie();
|
||||||
|
|
||||||
authMethod = meth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Change the access path from the default of {@link AccessPath#WEB_UI}. */
|
|
||||||
public void setAccessPath(AccessPath path) {
|
|
||||||
accessPath = path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Set the user account for this current request only. */
|
/** Set the user account for this current request only. */
|
||||||
public void setUserAccountId(Account.Id id, AuthMethod method) {
|
public void setUserAccountId(Account.Id id) {
|
||||||
key = new Key("id:" + id);
|
key = new Key("id:" + id);
|
||||||
val = new Val(id, 0, false, null, "", 0);
|
val = new Val(id, 0, false, null, 0);
|
||||||
authMethod = method;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void logout() {
|
public void logout() {
|
||||||
@@ -217,8 +211,4 @@ public final class CacheBasedWebSession implements WebSession {
|
|||||||
private static boolean isSecure(final HttpServletRequest req) {
|
private static boolean isSecure(final HttpServletRequest req) {
|
||||||
return req.isSecure() || "https".equals(req.getScheme());
|
return req.isSecure() || "https".equals(req.getScheme());
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthMethod getAuthMethod() {
|
|
||||||
return authMethod;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -19,7 +19,6 @@ import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
|
|||||||
|
|
||||||
import com.google.gerrit.server.account.AccountCache;
|
import com.google.gerrit.server.account.AccountCache;
|
||||||
import com.google.gerrit.server.account.AccountState;
|
import com.google.gerrit.server.account.AccountState;
|
||||||
import com.google.gerrit.server.account.AuthMethod;
|
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
@@ -100,9 +99,7 @@ class ContainerAuthFilter implements Filter {
|
|||||||
rsp.sendError(SC_UNAUTHORIZED);
|
rsp.sendError(SC_UNAUTHORIZED);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
session.get().setUserAccountId(
|
session.get().setUserAccountId(who.getAccount().getId());
|
||||||
who.getAccount().getId(),
|
|
||||||
AuthMethod.PASSWORD);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -18,7 +18,6 @@ import com.google.common.cache.Cache;
|
|||||||
import com.google.gerrit.common.data.Capable;
|
import com.google.gerrit.common.data.Capable;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.AccessPath;
|
|
||||||
import com.google.gerrit.server.AnonymousUser;
|
import com.google.gerrit.server.AnonymousUser;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.cache.CacheModule;
|
import com.google.gerrit.server.cache.CacheModule;
|
||||||
@@ -178,12 +177,10 @@ public class GitOverHttpServlet extends GitServlet {
|
|||||||
|
|
||||||
static class UploadFactory implements UploadPackFactory<HttpServletRequest> {
|
static class UploadFactory implements UploadPackFactory<HttpServletRequest> {
|
||||||
private final TransferConfig config;
|
private final TransferConfig config;
|
||||||
private final Provider<WebSession> session;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
UploadFactory(TransferConfig tc, Provider<WebSession> session) {
|
UploadFactory(TransferConfig tc) {
|
||||||
this.config = tc;
|
this.config = tc;
|
||||||
this.session = session;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -191,7 +188,6 @@ public class GitOverHttpServlet extends GitServlet {
|
|||||||
UploadPack up = new UploadPack(repo);
|
UploadPack up = new UploadPack(repo);
|
||||||
up.setPackConfig(config.getPackConfig());
|
up.setPackConfig(config.getPackConfig());
|
||||||
up.setTimeout(config.getTimeout());
|
up.setTimeout(config.getTimeout());
|
||||||
session.get().setAccessPath(AccessPath.GIT);
|
|
||||||
return up;
|
return up;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -241,14 +237,11 @@ public class GitOverHttpServlet extends GitServlet {
|
|||||||
|
|
||||||
static class ReceiveFactory implements ReceivePackFactory<HttpServletRequest> {
|
static class ReceiveFactory implements ReceivePackFactory<HttpServletRequest> {
|
||||||
private final AsyncReceiveCommits.Factory factory;
|
private final AsyncReceiveCommits.Factory factory;
|
||||||
private final Provider<WebSession> session;
|
|
||||||
private final TransferConfig config;
|
private final TransferConfig config;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ReceiveFactory(AsyncReceiveCommits.Factory factory,
|
ReceiveFactory(AsyncReceiveCommits.Factory factory, TransferConfig config) {
|
||||||
Provider<WebSession> session, TransferConfig config) {
|
|
||||||
this.factory = factory;
|
this.factory = factory;
|
||||||
this.session = session;
|
|
||||||
this.config = config;
|
this.config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,7 +262,6 @@ public class GitOverHttpServlet extends GitServlet {
|
|||||||
rp.setTimeout(config.getTimeout());
|
rp.setTimeout(config.getTimeout());
|
||||||
rp.setMaxObjectSizeLimit(config.getMaxObjectSizeLimit());
|
rp.setMaxObjectSizeLimit(config.getMaxObjectSizeLimit());
|
||||||
req.setAttribute(ATT_RC, rc);
|
req.setAttribute(ATT_RC, rc);
|
||||||
session.get().setAccessPath(AccessPath.GIT);
|
|
||||||
return rp;
|
return rp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -77,7 +77,7 @@ class HttpLogoutServlet extends HttpServlet {
|
|||||||
protected void doGet(final HttpServletRequest req,
|
protected void doGet(final HttpServletRequest req,
|
||||||
final HttpServletResponse rsp) throws IOException {
|
final HttpServletResponse rsp) throws IOException {
|
||||||
|
|
||||||
final String sid = webSession.get().getToken();
|
final String sid = webSession.get().getAccessToken();
|
||||||
final CurrentUser currentUser = webSession.get().getCurrentUser();
|
final CurrentUser currentUser = webSession.get().getCurrentUser();
|
||||||
final String what = "sign out";
|
final String what = "sign out";
|
||||||
final long when = System.currentTimeMillis();
|
final long when = System.currentTimeMillis();
|
||||||
|
@@ -22,7 +22,6 @@ import com.google.gerrit.server.account.AccountCache;
|
|||||||
import com.google.gerrit.server.account.AccountException;
|
import com.google.gerrit.server.account.AccountException;
|
||||||
import com.google.gerrit.server.account.AccountManager;
|
import com.google.gerrit.server.account.AccountManager;
|
||||||
import com.google.gerrit.server.account.AccountState;
|
import com.google.gerrit.server.account.AccountState;
|
||||||
import com.google.gerrit.server.account.AuthMethod;
|
|
||||||
import com.google.gerrit.server.account.AuthRequest;
|
import com.google.gerrit.server.account.AuthRequest;
|
||||||
import com.google.gerrit.server.account.AuthResult;
|
import com.google.gerrit.server.account.AuthResult;
|
||||||
import com.google.gerrit.server.config.AuthConfig;
|
import com.google.gerrit.server.config.AuthConfig;
|
||||||
@@ -143,8 +142,7 @@ class ProjectBasicAuthFilter implements Filter {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
AuthResult whoAuthResult = accountManager.authenticate(whoAuth);
|
AuthResult whoAuthResult = accountManager.authenticate(whoAuth);
|
||||||
session.get().setUserAccountId(whoAuthResult.getAccountId(),
|
session.get().setUserAccountId(whoAuthResult.getAccountId());
|
||||||
AuthMethod.PASSWORD);
|
|
||||||
return true;
|
return true;
|
||||||
} catch (AccountException e) {
|
} catch (AccountException e) {
|
||||||
log.warn("Authentication failed for " + username, e);
|
log.warn("Authentication failed for " + username, e);
|
||||||
|
@@ -22,7 +22,6 @@ import static javax.servlet.http.HttpServletResponse.SC_UNAUTHORIZED;
|
|||||||
|
|
||||||
import com.google.gerrit.server.account.AccountCache;
|
import com.google.gerrit.server.account.AccountCache;
|
||||||
import com.google.gerrit.server.account.AccountState;
|
import com.google.gerrit.server.account.AccountState;
|
||||||
import com.google.gerrit.server.account.AuthMethod;
|
|
||||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||||
import com.google.gerrit.server.config.GerritServerConfig;
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
import com.google.gwtjsonrpc.server.SignedToken;
|
import com.google.gwtjsonrpc.server.SignedToken;
|
||||||
@@ -165,9 +164,7 @@ class ProjectDigestFilter implements Filter {
|
|||||||
if (expect.equals(response)) {
|
if (expect.equals(response)) {
|
||||||
try {
|
try {
|
||||||
if (tokens.checkToken(nonce, "") != null) {
|
if (tokens.checkToken(nonce, "") != null) {
|
||||||
session.get().setUserAccountId(
|
session.get().setUserAccountId(who.getAccount().getId());
|
||||||
who.getAccount().getId(),
|
|
||||||
AuthMethod.PASSWORD);
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@@ -20,6 +20,8 @@ import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
|
|||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.gerrit.extensions.annotations.RequiresCapability;
|
import com.google.gerrit.extensions.annotations.RequiresCapability;
|
||||||
|
import com.google.gerrit.server.AccessPath;
|
||||||
|
import com.google.gerrit.server.AnonymousUser;
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.account.CapabilityControl;
|
import com.google.gerrit.server.account.CapabilityControl;
|
||||||
@@ -90,8 +92,11 @@ public abstract class RestApiServlet extends HttpServlet {
|
|||||||
res.setHeader("Content-Disposition", "attachment");
|
res.setHeader("Content-Disposition", "attachment");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
checkUserSession(req);
|
||||||
checkRequiresCapability();
|
checkRequiresCapability();
|
||||||
super.service(req, res);
|
super.service(req, res);
|
||||||
|
} catch (InvalidAuthException err) {
|
||||||
|
sendError(res, SC_FORBIDDEN, err.getMessage());
|
||||||
} catch (RequireCapabilityException err) {
|
} catch (RequireCapabilityException err) {
|
||||||
sendError(res, SC_FORBIDDEN, err.getMessage());
|
sendError(res, SC_FORBIDDEN, err.getMessage());
|
||||||
} catch (Error err) {
|
} catch (Error err) {
|
||||||
@@ -101,6 +106,18 @@ public abstract class RestApiServlet extends HttpServlet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkUserSession(HttpServletRequest req)
|
||||||
|
throws InvalidAuthException {
|
||||||
|
CurrentUser user = currentUser.get();
|
||||||
|
if (user instanceof AnonymousUser) {
|
||||||
|
if (!"GET".equals(req.getMethod())) {
|
||||||
|
throw new InvalidAuthException("Authentication required");
|
||||||
|
}
|
||||||
|
} else if (user.getAccessPath() != AccessPath.REST_API) {
|
||||||
|
throw new InvalidAuthException("Invalid authentication method");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void checkRequiresCapability() throws RequireCapabilityException {
|
private void checkRequiresCapability() throws RequireCapabilityException {
|
||||||
RequiresCapability rc = getClass().getAnnotation(RequiresCapability.class);
|
RequiresCapability rc = getClass().getAnnotation(RequiresCapability.class);
|
||||||
if (rc != null) {
|
if (rc != null) {
|
||||||
@@ -229,4 +246,11 @@ public abstract class RestApiServlet extends HttpServlet {
|
|||||||
super(msg);
|
super(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("serial") // Never serialized or thrown out of this class.
|
||||||
|
private static class InvalidAuthException extends Exception {
|
||||||
|
public InvalidAuthException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -16,31 +16,22 @@ package com.google.gerrit.httpd;
|
|||||||
|
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
import com.google.gerrit.reviewdb.client.AccountExternalId;
|
||||||
import com.google.gerrit.server.AccessPath;
|
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.account.AuthMethod;
|
|
||||||
import com.google.gerrit.server.account.AuthResult;
|
import com.google.gerrit.server.account.AuthResult;
|
||||||
|
|
||||||
public interface WebSession {
|
public interface WebSession {
|
||||||
public AuthMethod getAuthMethod();
|
|
||||||
|
|
||||||
public boolean isSignedIn();
|
public boolean isSignedIn();
|
||||||
|
|
||||||
public String getToken();
|
public String getAccessToken();
|
||||||
|
|
||||||
public boolean isTokenValid(String inputToken);
|
|
||||||
|
|
||||||
public AccountExternalId.Key getLastLoginExternalId();
|
public AccountExternalId.Key getLastLoginExternalId();
|
||||||
|
|
||||||
public CurrentUser getCurrentUser();
|
public CurrentUser getCurrentUser();
|
||||||
|
|
||||||
public void login(AuthResult res, AuthMethod meth, boolean rememberMe);
|
public void login(AuthResult res, boolean rememberMe);
|
||||||
|
|
||||||
/** Change the access path from the default of {@link AccessPath#WEB_UI}. */
|
|
||||||
public void setAccessPath(AccessPath path);
|
|
||||||
|
|
||||||
/** Set the user account for this current request only. */
|
/** Set the user account for this current request only. */
|
||||||
public void setUserAccountId(Account.Id id, AuthMethod method);
|
public void setUserAccountId(Account.Id id);
|
||||||
|
|
||||||
public void logout();
|
public void logout();
|
||||||
}
|
}
|
||||||
|
@@ -90,13 +90,11 @@ class WebSessionManager {
|
|||||||
final Account.Id who = val.getAccountId();
|
final Account.Id who = val.getAccountId();
|
||||||
final boolean remember = val.isPersistentCookie();
|
final boolean remember = val.isPersistentCookie();
|
||||||
final AccountExternalId.Key lastLogin = val.getExternalId();
|
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,
|
Val createVal(final Key key, final Account.Id who, final boolean remember,
|
||||||
final AccountExternalId.Key lastLogin, String xsrfToken) {
|
final AccountExternalId.Key lastLogin) {
|
||||||
// Refresh the cookie every hour or when it is half-expired.
|
// Refresh the cookie every hour or when it is half-expired.
|
||||||
// This reduces the odds that the user session will be kicked
|
// This reduces the odds that the user session will be kicked
|
||||||
// early but also avoids us needing to refresh the cookie on
|
// early but also avoids us needing to refresh the cookie on
|
||||||
@@ -109,17 +107,7 @@ class WebSessionManager {
|
|||||||
final long refreshCookieAt = now + refresh;
|
final long refreshCookieAt = now + refresh;
|
||||||
final long expiresAt = now + sessionMaxAgeMillis;
|
final long expiresAt = now + sessionMaxAgeMillis;
|
||||||
|
|
||||||
if (xsrfToken == null) {
|
Val val = new Val(who, refreshCookieAt, remember, lastLogin, expiresAt);
|
||||||
// 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, expiresAt);
|
|
||||||
self.put(key.token, val);
|
self.put(key.token, val);
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
@@ -182,18 +170,15 @@ class WebSessionManager {
|
|||||||
private transient long refreshCookieAt;
|
private transient long refreshCookieAt;
|
||||||
private transient boolean persistentCookie;
|
private transient boolean persistentCookie;
|
||||||
private transient AccountExternalId.Key externalId;
|
private transient AccountExternalId.Key externalId;
|
||||||
private transient String xsrfToken;
|
|
||||||
private transient long expiresAt;
|
private transient long expiresAt;
|
||||||
|
|
||||||
Val(final Account.Id accountId, final long refreshCookieAt,
|
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,
|
|
||||||
final long expiresAt) {
|
final long expiresAt) {
|
||||||
this.accountId = accountId;
|
this.accountId = accountId;
|
||||||
this.refreshCookieAt = refreshCookieAt;
|
this.refreshCookieAt = refreshCookieAt;
|
||||||
this.persistentCookie = persistentCookie;
|
this.persistentCookie = persistentCookie;
|
||||||
this.externalId = externalId;
|
this.externalId = externalId;
|
||||||
this.xsrfToken = xsrfToken;
|
|
||||||
this.expiresAt = expiresAt;
|
this.expiresAt = expiresAt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,10 +198,6 @@ class WebSessionManager {
|
|||||||
return persistentCookie;
|
return persistentCookie;
|
||||||
}
|
}
|
||||||
|
|
||||||
String getXsrfToken() {
|
|
||||||
return xsrfToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeObject(final ObjectOutputStream out) throws IOException {
|
private void writeObject(final ObjectOutputStream out) throws IOException {
|
||||||
writeVarInt32(out, 1);
|
writeVarInt32(out, 1);
|
||||||
writeVarInt32(out, accountId.get());
|
writeVarInt32(out, accountId.get());
|
||||||
@@ -232,9 +213,6 @@ class WebSessionManager {
|
|||||||
writeString(out, externalId.get());
|
writeString(out, externalId.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
writeVarInt32(out, 5);
|
|
||||||
writeString(out, xsrfToken);
|
|
||||||
|
|
||||||
writeVarInt32(out, 6);
|
writeVarInt32(out, 6);
|
||||||
writeFixInt64(out, expiresAt);
|
writeFixInt64(out, expiresAt);
|
||||||
|
|
||||||
@@ -260,7 +238,7 @@ class WebSessionManager {
|
|||||||
externalId = new AccountExternalId.Key(readString(in));
|
externalId = new AccountExternalId.Key(readString(in));
|
||||||
continue;
|
continue;
|
||||||
case 5:
|
case 5:
|
||||||
xsrfToken = readString(in);
|
readString(in);
|
||||||
continue;
|
continue;
|
||||||
case 6:
|
case 6:
|
||||||
expiresAt = readFixInt64(in);
|
expiresAt = readFixInt64(in);
|
||||||
|
@@ -24,7 +24,6 @@ import com.google.gerrit.reviewdb.client.AccountExternalId;
|
|||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.account.AccountException;
|
import com.google.gerrit.server.account.AccountException;
|
||||||
import com.google.gerrit.server.account.AccountManager;
|
import com.google.gerrit.server.account.AccountManager;
|
||||||
import com.google.gerrit.server.account.AuthMethod;
|
|
||||||
import com.google.gerrit.server.account.AuthRequest;
|
import com.google.gerrit.server.account.AuthRequest;
|
||||||
import com.google.gerrit.server.account.AuthResult;
|
import com.google.gerrit.server.account.AuthResult;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
@@ -114,7 +113,7 @@ public class BecomeAnyAccountLoginServlet extends HttpServlet {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (res != null) {
|
if (res != null) {
|
||||||
webSession.get().login(res, AuthMethod.BACKDOOR, false);
|
webSession.get().login(res, false);
|
||||||
final StringBuilder rdr = new StringBuilder();
|
final StringBuilder rdr = new StringBuilder();
|
||||||
rdr.append(req.getContextPath());
|
rdr.append(req.getContextPath());
|
||||||
if (IS_DEV && req.getParameter("gwt.codesvr") != null) {
|
if (IS_DEV && req.getParameter("gwt.codesvr") != null) {
|
||||||
|
@@ -19,7 +19,6 @@ import com.google.gerrit.httpd.HtmlDomUtil;
|
|||||||
import com.google.gerrit.httpd.WebSession;
|
import com.google.gerrit.httpd.WebSession;
|
||||||
import com.google.gerrit.server.account.AccountException;
|
import com.google.gerrit.server.account.AccountException;
|
||||||
import com.google.gerrit.server.account.AccountManager;
|
import com.google.gerrit.server.account.AccountManager;
|
||||||
import com.google.gerrit.server.account.AuthMethod;
|
|
||||||
import com.google.gerrit.server.account.AuthRequest;
|
import com.google.gerrit.server.account.AuthRequest;
|
||||||
import com.google.gerrit.server.account.AuthResult;
|
import com.google.gerrit.server.account.AuthResult;
|
||||||
import com.google.gerrit.server.config.AuthConfig;
|
import com.google.gerrit.server.config.AuthConfig;
|
||||||
@@ -136,8 +135,7 @@ class HttpLoginServlet extends HttpServlet {
|
|||||||
}
|
}
|
||||||
rdr.append(token);
|
rdr.append(token);
|
||||||
|
|
||||||
webSession.get().login(arsp, AuthMethod.COOKIE,
|
webSession.get().login(arsp, true /* persistent cookie */);
|
||||||
true /* persistent cookie */);
|
|
||||||
rsp.sendRedirect(rdr.toString());
|
rsp.sendRedirect(rdr.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -17,7 +17,6 @@ package com.google.gerrit.httpd.auth.container;
|
|||||||
import com.google.gerrit.httpd.WebSession;
|
import com.google.gerrit.httpd.WebSession;
|
||||||
import com.google.gerrit.server.account.AccountException;
|
import com.google.gerrit.server.account.AccountException;
|
||||||
import com.google.gerrit.server.account.AccountManager;
|
import com.google.gerrit.server.account.AccountManager;
|
||||||
import com.google.gerrit.server.account.AuthMethod;
|
|
||||||
import com.google.gerrit.server.account.AuthRequest;
|
import com.google.gerrit.server.account.AuthRequest;
|
||||||
import com.google.gerrit.server.account.AuthResult;
|
import com.google.gerrit.server.account.AuthResult;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
@@ -85,7 +84,7 @@ class HttpsClientSslCertAuthFilter implements Filter {
|
|||||||
log.error(err, e);
|
log.error(err, e);
|
||||||
throw new ServletException(err, e);
|
throw new ServletException(err, e);
|
||||||
}
|
}
|
||||||
webSession.get().login(arsp, AuthMethod.COOKIE, true);
|
webSession.get().login(arsp, true);
|
||||||
chain.doFilter(req, rsp);
|
chain.doFilter(req, rsp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -21,7 +21,6 @@ import com.google.gerrit.reviewdb.client.AuthType;
|
|||||||
import com.google.gerrit.server.account.AccountException;
|
import com.google.gerrit.server.account.AccountException;
|
||||||
import com.google.gerrit.server.account.AccountManager;
|
import com.google.gerrit.server.account.AccountManager;
|
||||||
import com.google.gerrit.server.account.AccountUserNameException;
|
import com.google.gerrit.server.account.AccountUserNameException;
|
||||||
import com.google.gerrit.server.account.AuthMethod;
|
|
||||||
import com.google.gerrit.server.account.AuthRequest;
|
import com.google.gerrit.server.account.AuthRequest;
|
||||||
import com.google.gerrit.server.account.AuthResult;
|
import com.google.gerrit.server.account.AuthResult;
|
||||||
import com.google.gerrit.server.auth.AuthenticationUnavailableException;
|
import com.google.gerrit.server.auth.AuthenticationUnavailableException;
|
||||||
@@ -87,8 +86,7 @@ class UserPassAuthServiceImpl implements UserPassAuthService {
|
|||||||
|
|
||||||
result.success = true;
|
result.success = true;
|
||||||
result.isNew = res.isNew();
|
result.isNew = res.isNew();
|
||||||
webSession.get().login(res, AuthMethod.PASSWORD,
|
webSession.get().login(res, true /* persistent cookie */);
|
||||||
true /* persistent cookie */);
|
|
||||||
callback.onSuccess(result);
|
callback.onSuccess(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -178,8 +178,8 @@ public class HostPageServlet extends HttpServlet {
|
|||||||
json(((IdentifiedUser) user).getAccount(), w);
|
json(((IdentifiedUser) user).getAccount(), w);
|
||||||
w.write(";");
|
w.write(";");
|
||||||
|
|
||||||
w.write(HPD_ID + ".xsrfToken=");
|
w.write(HPD_ID + ".accessToken=");
|
||||||
json(session.get().getToken(), w);
|
json(session.get().getAccessToken(), w);
|
||||||
w.write(";");
|
w.write(";");
|
||||||
|
|
||||||
w.write(HPD_ID + ".accountDiffPref=");
|
w.write(HPD_ID + ".accountDiffPref=");
|
||||||
|
@@ -131,7 +131,7 @@ final class GerritJsonServlet extends JsonServlet<GerritJsonServlet.GerritCall>
|
|||||||
}
|
}
|
||||||
Audit note = (Audit) method.getAnnotation(Audit.class);
|
Audit note = (Audit) method.getAnnotation(Audit.class);
|
||||||
if (note != null) {
|
if (note != null) {
|
||||||
final String sid = call.getWebSession().getToken();
|
final String sid = call.getWebSession().getAccessToken();
|
||||||
final CurrentUser username = call.getWebSession().getCurrentUser();
|
final CurrentUser username = call.getWebSession().getCurrentUser();
|
||||||
final List<Object> args =
|
final List<Object> args =
|
||||||
extractParams(note, call);
|
extractParams(note, call);
|
||||||
@@ -249,7 +249,7 @@ final class GerritJsonServlet extends JsonServlet<GerritJsonServlet.GerritCall>
|
|||||||
} else {
|
} else {
|
||||||
// The session must exist, and must be using this token.
|
// The session must exist, and must be using this token.
|
||||||
//
|
//
|
||||||
return session.isSignedIn() && session.isTokenValid(keyIn);
|
return session.isSignedIn() && keyIn.equals(session.getAccessToken());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -26,7 +26,6 @@ import com.google.gerrit.server.IdentifiedUser;
|
|||||||
import com.google.gerrit.server.UrlEncoded;
|
import com.google.gerrit.server.UrlEncoded;
|
||||||
import com.google.gerrit.server.account.AccountException;
|
import com.google.gerrit.server.account.AccountException;
|
||||||
import com.google.gerrit.server.account.AccountManager;
|
import com.google.gerrit.server.account.AccountManager;
|
||||||
import com.google.gerrit.server.account.AuthMethod;
|
|
||||||
import com.google.gerrit.server.config.AuthConfig;
|
import com.google.gerrit.server.config.AuthConfig;
|
||||||
import com.google.gerrit.server.config.CanonicalWebUrl;
|
import com.google.gerrit.server.config.CanonicalWebUrl;
|
||||||
import com.google.gerrit.server.config.ConfigUtil;
|
import com.google.gerrit.server.config.ConfigUtil;
|
||||||
@@ -417,7 +416,7 @@ class OpenIdServiceImpl implements OpenIdService {
|
|||||||
lastId.setMaxAge(0);
|
lastId.setMaxAge(0);
|
||||||
}
|
}
|
||||||
rsp.addCookie(lastId);
|
rsp.addCookie(lastId);
|
||||||
webSession.get().login(arsp, AuthMethod.COOKIE, remember);
|
webSession.get().login(arsp, remember);
|
||||||
if (arsp.isNew() && claimedIdentifier != null) {
|
if (arsp.isNew() && claimedIdentifier != null) {
|
||||||
final com.google.gerrit.server.account.AuthRequest linkReq =
|
final com.google.gerrit.server.account.AuthRequest linkReq =
|
||||||
new com.google.gerrit.server.account.AuthRequest(
|
new com.google.gerrit.server.account.AuthRequest(
|
||||||
@@ -431,7 +430,7 @@ class OpenIdServiceImpl implements OpenIdService {
|
|||||||
|
|
||||||
case LINK_IDENTIY: {
|
case LINK_IDENTIY: {
|
||||||
arsp = accountManager.link(identifiedUser.get().getAccountId(), areq);
|
arsp = accountManager.link(identifiedUser.get().getAccountId(), areq);
|
||||||
webSession.get().login(arsp, AuthMethod.COOKIE, remember);
|
webSession.get().login(arsp, remember);
|
||||||
callback(false, req, rsp);
|
callback(false, req, rsp);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -19,8 +19,11 @@ public enum AccessPath {
|
|||||||
/** An unknown access path, probably should not be special. */
|
/** An unknown access path, probably should not be special. */
|
||||||
UNKNOWN,
|
UNKNOWN,
|
||||||
|
|
||||||
/** Access through the web UI. */
|
/** Access through the REST API. */
|
||||||
WEB_UI,
|
REST_API,
|
||||||
|
|
||||||
|
/** Access by a web cookie. This path is not protected like REST_API. */
|
||||||
|
WEB_BROWSER,
|
||||||
|
|
||||||
/** Access through an SSH command that is not invoked by Git. */
|
/** Access through an SSH command that is not invoked by Git. */
|
||||||
SSH_COMMAND,
|
SSH_COMMAND,
|
||||||
|
@@ -1,30 +0,0 @@
|
|||||||
// Copyright (C) 2012 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.server.account;
|
|
||||||
|
|
||||||
/** Method by which a user has authenticated for a given request. */
|
|
||||||
public enum AuthMethod {
|
|
||||||
/** The user is not authenticated */
|
|
||||||
NONE,
|
|
||||||
|
|
||||||
/** The user is authenticated via a cookie. */
|
|
||||||
COOKIE,
|
|
||||||
|
|
||||||
/** The user authenticated with a password for this request. */
|
|
||||||
PASSWORD,
|
|
||||||
|
|
||||||
/** The user has used a credentialess development feature to login. */
|
|
||||||
BACKDOOR;
|
|
||||||
}
|
|
@@ -218,7 +218,7 @@ public class RefControl {
|
|||||||
}
|
}
|
||||||
boolean owner;
|
boolean owner;
|
||||||
switch (getCurrentUser().getAccessPath()) {
|
switch (getCurrentUser().getAccessPath()) {
|
||||||
case WEB_UI:
|
case REST_API:
|
||||||
owner = isOwner();
|
owner = isOwner();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -285,7 +285,7 @@ public class RefControl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (getCurrentUser().getAccessPath()) {
|
switch (getCurrentUser().getAccessPath()) {
|
||||||
case WEB_UI:
|
case REST_API:
|
||||||
return isOwner() || canPushWithForce();
|
return isOwner() || canPushWithForce();
|
||||||
|
|
||||||
case GIT:
|
case GIT:
|
||||||
|
Reference in New Issue
Block a user