Merge branch 'stable-2.10' into stable-2.11
* stable-2.10: Update version to 2.10.2 Release notes for Gerrit 2.10.2 Do not return 403 when clicking on Gitweb breadcrumb Add log messages to troubleshoot OAuth/OpenID linking Remove unused OAuthToken in authorisation URL OnlineReindexer: log the success/failure numbers on exit Update replication plugin OAuth: Allow to link claimed identity to existing accounts OAuth: Allow to change username Change-Id: Ia9fc371b9f957c8e0fc3e215084baa3d31dadd41
This commit is contained in:
31
ReleaseNotes/ReleaseNotes-2.10.2.txt
Normal file
31
ReleaseNotes/ReleaseNotes-2.10.2.txt
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
Release notes for Gerrit 2.10.2
|
||||||
|
===============================
|
||||||
|
|
||||||
|
There are no schema changes from link:ReleaseNotes-2.10.1.html[2.10.1].
|
||||||
|
|
||||||
|
Download:
|
||||||
|
link:https://gerrit-releases.storage.googleapis.com/gerrit-2.10.2.war[
|
||||||
|
https://gerrit-releases.storage.googleapis.com/gerrit-2.10.2.war]
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Work around MyersDiff infinite loop in PatchListLoader. If the MyersDiff diff
|
||||||
|
doesn't finish within 5 seconds, interrupt it and fall back to a different diff
|
||||||
|
algorithm. From the user perspective, the only difference when the infinite
|
||||||
|
loop is detected is that the files in the commit will not be compared in-depth,
|
||||||
|
which will result in bigger edit regions.
|
||||||
|
|
||||||
|
Secondary Index
|
||||||
|
---------------
|
||||||
|
|
||||||
|
* Online reindexing: log the number of done/failed changes in the error_log.
|
||||||
|
Administrators can use the logged information to decide whether to activate the
|
||||||
|
new index version or not.
|
||||||
|
|
||||||
|
Gitweb
|
||||||
|
------
|
||||||
|
|
||||||
|
* Do not return `Forbidden` when clicking on Gitweb breadcrumb. Now when the
|
||||||
|
user clicks on the parent folder, redirect to Gerrit projects list screen with
|
||||||
|
the parent folder path as the filter.
|
||||||
@@ -9,6 +9,7 @@ Version 2.11.x
|
|||||||
[[2_10]]
|
[[2_10]]
|
||||||
Version 2.10.x
|
Version 2.10.x
|
||||||
--------------
|
--------------
|
||||||
|
* link:ReleaseNotes-2.10.2.html[2.10.2]
|
||||||
* link:ReleaseNotes-2.10.1.html[2.10.1]
|
* link:ReleaseNotes-2.10.1.html[2.10.1]
|
||||||
* link:ReleaseNotes-2.10.html[2.10]
|
* link:ReleaseNotes-2.10.html[2.10]
|
||||||
|
|
||||||
|
|||||||
@@ -22,30 +22,21 @@ import java.io.IOException;
|
|||||||
@ExtensionPoint
|
@ExtensionPoint
|
||||||
public interface OAuthServiceProvider {
|
public interface OAuthServiceProvider {
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the request token.
|
|
||||||
*
|
|
||||||
* @return request token
|
|
||||||
*/
|
|
||||||
OAuthToken getRequestToken();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the URL where you should redirect your users to authenticate
|
* Returns the URL where you should redirect your users to authenticate
|
||||||
* your application.
|
* your application.
|
||||||
*
|
*
|
||||||
* @param requestToken the request token you need to authorize
|
* @return the OAuth service URL to redirect your users for authentication
|
||||||
* @return the URL where you should redirect your users
|
|
||||||
*/
|
*/
|
||||||
String getAuthorizationUrl(OAuthToken requestToken);
|
String getAuthorizationUrl();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the access token
|
* Retrieve the access token
|
||||||
*
|
*
|
||||||
* @param requestToken request token (obtained previously)
|
|
||||||
* @param verifier verifier code
|
* @param verifier verifier code
|
||||||
* @return access token
|
* @return access token
|
||||||
*/
|
*/
|
||||||
OAuthToken getAccessToken(OAuthToken requestToken, OAuthVerifier verifier);
|
OAuthToken getAccessToken(OAuthVerifier verifier);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* After establishing of secure communication channel, this method supossed to
|
* After establishing of secure communication channel, this method supossed to
|
||||||
|
|||||||
@@ -20,15 +20,18 @@ public class OAuthUserInfo {
|
|||||||
private final String userName;
|
private final String userName;
|
||||||
private final String emailAddress;
|
private final String emailAddress;
|
||||||
private final String displayName;
|
private final String displayName;
|
||||||
|
private final String claimedIdentity;
|
||||||
|
|
||||||
public OAuthUserInfo(String externalId,
|
public OAuthUserInfo(String externalId,
|
||||||
String userName,
|
String userName,
|
||||||
String emailAddress,
|
String emailAddress,
|
||||||
String displayName) {
|
String displayName,
|
||||||
|
String claimedIdentity) {
|
||||||
this.externalId = externalId;
|
this.externalId = externalId;
|
||||||
this.userName = userName;
|
this.userName = userName;
|
||||||
this.emailAddress = emailAddress;
|
this.emailAddress = emailAddress;
|
||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
|
this.claimedIdentity = claimedIdentity;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getExternalId() {
|
public String getExternalId() {
|
||||||
@@ -46,4 +49,8 @@ public class OAuthUserInfo {
|
|||||||
public String getDisplayName() {
|
public String getDisplayName() {
|
||||||
return displayName;
|
return displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getClaimedIdentity() {
|
||||||
|
return claimedIdentity;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,7 +79,9 @@ public class OnlineReindexer {
|
|||||||
SiteIndexer.Result result =
|
SiteIndexer.Result result =
|
||||||
batchIndexer.indexAll(index, projectCache.all());
|
batchIndexer.indexAll(index, projectCache.all());
|
||||||
if (!result.success()) {
|
if (!result.success()) {
|
||||||
log.error("Online reindex of schema version {} failed", version(index));
|
log.error("Online reindex of schema version {} failed. Successfully"
|
||||||
|
+ " indexed {} changes, failed to index {} changes",
|
||||||
|
version(index), result.doneCount(), result.failedCount());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,9 +11,11 @@ java_library(
|
|||||||
'//gerrit-common:annotations',
|
'//gerrit-common:annotations',
|
||||||
'//gerrit-extension-api:api',
|
'//gerrit-extension-api:api',
|
||||||
'//gerrit-httpd:httpd',
|
'//gerrit-httpd:httpd',
|
||||||
|
'//gerrit-reviewdb:server',
|
||||||
'//gerrit-server:server',
|
'//gerrit-server:server',
|
||||||
'//lib:gson',
|
'//lib:gson',
|
||||||
'//lib:guava',
|
'//lib:guava',
|
||||||
|
'//lib:gwtorm',
|
||||||
'//lib/commons:codec',
|
'//lib/commons:codec',
|
||||||
'//lib/guice:guice',
|
'//lib/guice:guice',
|
||||||
'//lib/guice:guice-servlet',
|
'//lib/guice:guice-servlet',
|
||||||
|
|||||||
@@ -23,9 +23,11 @@ import com.google.gerrit.extensions.auth.oauth.OAuthUserInfo;
|
|||||||
import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
|
import com.google.gerrit.extensions.auth.oauth.OAuthVerifier;
|
||||||
import com.google.gerrit.extensions.registration.DynamicItem;
|
import com.google.gerrit.extensions.registration.DynamicItem;
|
||||||
import com.google.gerrit.httpd.WebSession;
|
import com.google.gerrit.httpd.WebSession;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
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.AuthResult;
|
import com.google.gerrit.server.account.AuthResult;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.servlet.SessionScoped;
|
import com.google.inject.servlet.SessionScoped;
|
||||||
|
|
||||||
@@ -87,8 +89,7 @@ class OAuthSession {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.debug("Login-Retrieve-User " + this);
|
log.debug("Login-Retrieve-User " + this);
|
||||||
token = oauth.getAccessToken(null,
|
token = oauth.getAccessToken(new OAuthVerifier(request.getParameter("code")));
|
||||||
new OAuthVerifier(request.getParameter("code")));
|
|
||||||
|
|
||||||
user = oauth.getUserInfo(token);
|
user = oauth.getUserInfo(token);
|
||||||
|
|
||||||
@@ -103,7 +104,7 @@ class OAuthSession {
|
|||||||
} else {
|
} else {
|
||||||
log.debug("Login-PHASE1 " + this);
|
log.debug("Login-PHASE1 " + this);
|
||||||
redirectUrl = request.getRequestURI();
|
redirectUrl = request.getRequestURI();
|
||||||
response.sendRedirect(oauth.getAuthorizationUrl(null) +
|
response.sendRedirect(oauth.getAuthorizationUrl() +
|
||||||
"&state=" + state);
|
"&state=" + state);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -113,11 +114,48 @@ class OAuthSession {
|
|||||||
throws IOException {
|
throws IOException {
|
||||||
com.google.gerrit.server.account.AuthRequest areq =
|
com.google.gerrit.server.account.AuthRequest areq =
|
||||||
new com.google.gerrit.server.account.AuthRequest(user.getExternalId());
|
new com.google.gerrit.server.account.AuthRequest(user.getExternalId());
|
||||||
areq.setUserName(user.getUserName());
|
|
||||||
areq.setEmailAddress(user.getEmailAddress());
|
|
||||||
areq.setDisplayName(user.getDisplayName());
|
|
||||||
AuthResult arsp;
|
AuthResult arsp;
|
||||||
try {
|
try {
|
||||||
|
String claimedIdentifier = user.getClaimedIdentity();
|
||||||
|
Account.Id actualId = accountManager.lookup(user.getExternalId());
|
||||||
|
if (!Strings.isNullOrEmpty(claimedIdentifier)) {
|
||||||
|
Account.Id claimedId = accountManager.lookup(claimedIdentifier);
|
||||||
|
if (claimedId != null && actualId != null) {
|
||||||
|
if (claimedId.equals(actualId)) {
|
||||||
|
// Both link to the same account, that's what we expected.
|
||||||
|
log.debug("OAuth2: claimed identity equals current id");
|
||||||
|
} else {
|
||||||
|
// This is (for now) a fatal error. There are two records
|
||||||
|
// for what might be the same user.
|
||||||
|
//
|
||||||
|
log.error("OAuth accounts disagree over user identity:\n"
|
||||||
|
+ " Claimed ID: " + claimedId + " is " + claimedIdentifier
|
||||||
|
+ "\n" + " Delgate ID: " + actualId + " is "
|
||||||
|
+ user.getExternalId());
|
||||||
|
rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (claimedId != null && actualId == null) {
|
||||||
|
// Claimed account already exists: link to it.
|
||||||
|
//
|
||||||
|
log.info("OAuth2: linking claimed identity to {}",
|
||||||
|
claimedId.toString());
|
||||||
|
try {
|
||||||
|
accountManager.link(claimedId, areq);
|
||||||
|
} catch (OrmException e) {
|
||||||
|
log.error("Cannot link: " + user.getExternalId()
|
||||||
|
+ " to user identity:\n"
|
||||||
|
+ " Claimed ID: " + claimedId + " is " + claimedIdentifier);
|
||||||
|
rsp.sendError(HttpServletResponse.SC_FORBIDDEN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.debug("OAuth2: claimed identity is empty");
|
||||||
|
}
|
||||||
|
areq.setUserName(user.getUserName());
|
||||||
|
areq.setEmailAddress(user.getEmailAddress());
|
||||||
|
areq.setDisplayName(user.getDisplayName());
|
||||||
arsp = accountManager.authenticate(areq);
|
arsp = accountManager.authenticate(areq);
|
||||||
} catch (AccountException e) {
|
} catch (AccountException e) {
|
||||||
log.error("Unable to authenticate user \"" + user + "\"", e);
|
log.error("Unable to authenticate user \"" + user + "\"", e);
|
||||||
|
|||||||
@@ -144,6 +144,7 @@ class OpenIdServiceImpl {
|
|||||||
final AuthRequest aReq;
|
final AuthRequest aReq;
|
||||||
try {
|
try {
|
||||||
aReq = manager.authenticate(state.discovered, state.retTo.toString());
|
aReq = manager.authenticate(state.discovered, state.retTo.toString());
|
||||||
|
log.debug("OpenID: openid-realm={}", state.contextUrl);
|
||||||
aReq.setRealm(state.contextUrl);
|
aReq.setRealm(state.contextUrl);
|
||||||
|
|
||||||
if (requestRegistration(aReq)) {
|
if (requestRegistration(aReq)) {
|
||||||
|
|||||||
@@ -40,8 +40,7 @@ public class DefaultRealm extends AbstractRealm {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean allowsEdit(final Account.FieldName field) {
|
public boolean allowsEdit(final Account.FieldName field) {
|
||||||
if (authConfig.getAuthType() == AuthType.HTTP
|
if (authConfig.getAuthType() == AuthType.HTTP) {
|
||||||
|| authConfig.getAuthType() == AuthType.OAUTH) {
|
|
||||||
switch (field) {
|
switch (field) {
|
||||||
case USER_NAME:
|
case USER_NAME:
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
Reference in New Issue
Block a user