Merge changes from topic 'notedb-accounts-1'
* changes: AccountsUpdate: Rename atomicUpdate to update AccountsUpdate: Rename update method to replace Always update accounts atomically Migrate accounts to NoteDb (part 2) Disallow updates to account.config by direct push or submit Migrate accounts to NoteDb (part 1) Let AccountsUpdate#insert create the Account instance AccountsUpdate: Remove upsert method
This commit is contained in:
commit
30446b1921
@ -19,7 +19,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||||
|
|
||||||
import com.google.gerrit.common.Nullable;
|
import com.google.gerrit.common.Nullable;
|
||||||
import com.google.gerrit.common.TimeUtil;
|
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroup;
|
import com.google.gerrit.reviewdb.client.AccountGroup;
|
||||||
import com.google.gerrit.reviewdb.client.AccountGroupMember;
|
import com.google.gerrit.reviewdb.client.AccountGroupMember;
|
||||||
@ -109,10 +108,15 @@ public class AccountCreator {
|
|||||||
}
|
}
|
||||||
externalIdsUpdate.create().insert(extIds);
|
externalIdsUpdate.create().insert(extIds);
|
||||||
|
|
||||||
Account a = new Account(id, TimeUtil.nowTs());
|
accountsUpdate
|
||||||
a.setFullName(fullName);
|
.create()
|
||||||
a.setPreferredEmail(email);
|
.insert(
|
||||||
accountsUpdate.create().insert(db, a);
|
db,
|
||||||
|
id,
|
||||||
|
a -> {
|
||||||
|
a.setFullName(fullName);
|
||||||
|
a.setPreferredEmail(email);
|
||||||
|
});
|
||||||
|
|
||||||
if (groups != null) {
|
if (groups != null) {
|
||||||
for (String n : groups) {
|
for (String n : groups) {
|
||||||
|
@ -32,6 +32,7 @@ import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
|
|||||||
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
|
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||||
import static java.util.stream.Collectors.toSet;
|
import static java.util.stream.Collectors.toSet;
|
||||||
|
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
|
||||||
import static org.junit.Assert.fail;
|
import static org.junit.Assert.fail;
|
||||||
|
|
||||||
import com.google.common.collect.FluentIterable;
|
import com.google.common.collect.FluentIterable;
|
||||||
@ -74,6 +75,7 @@ import com.google.gerrit.gpg.testutil.TestKey;
|
|||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.RefNames;
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
import com.google.gerrit.server.account.AccountByEmailCache;
|
import com.google.gerrit.server.account.AccountByEmailCache;
|
||||||
|
import com.google.gerrit.server.account.AccountConfig;
|
||||||
import com.google.gerrit.server.account.WatchConfig;
|
import com.google.gerrit.server.account.WatchConfig;
|
||||||
import com.google.gerrit.server.account.WatchConfig.NotifyType;
|
import com.google.gerrit.server.account.WatchConfig.NotifyType;
|
||||||
import com.google.gerrit.server.account.externalids.ExternalId;
|
import com.google.gerrit.server.account.externalids.ExternalId;
|
||||||
@ -91,7 +93,6 @@ import com.google.inject.Provider;
|
|||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
@ -106,6 +107,7 @@ import org.eclipse.jgit.api.errors.TransportException;
|
|||||||
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
|
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
|
||||||
import org.eclipse.jgit.junit.TestRepository;
|
import org.eclipse.jgit.junit.TestRepository;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
import org.eclipse.jgit.lib.ObjectReader;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.RefUpdate;
|
import org.eclipse.jgit.lib.RefUpdate;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
@ -114,6 +116,7 @@ import org.eclipse.jgit.revwalk.RevWalk;
|
|||||||
import org.eclipse.jgit.transport.PushCertificateIdent;
|
import org.eclipse.jgit.transport.PushCertificateIdent;
|
||||||
import org.eclipse.jgit.transport.PushResult;
|
import org.eclipse.jgit.transport.PushResult;
|
||||||
import org.eclipse.jgit.transport.RemoteRefUpdate;
|
import org.eclipse.jgit.transport.RemoteRefUpdate;
|
||||||
|
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
@ -162,8 +165,8 @@ public class AccountIT extends AbstractDaemonTest {
|
|||||||
externalIdsUpdate = externalIdsUpdateFactory.create();
|
externalIdsUpdate = externalIdsUpdateFactory.create();
|
||||||
|
|
||||||
savedExternalIds = new ArrayList<>();
|
savedExternalIds = new ArrayList<>();
|
||||||
savedExternalIds.addAll(getExternalIds(admin));
|
savedExternalIds.addAll(externalIds.byAccount(admin.id));
|
||||||
savedExternalIds.addAll(getExternalIds(user));
|
savedExternalIds.addAll(externalIds.byAccount(user.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
@ -172,8 +175,8 @@ public class AccountIT extends AbstractDaemonTest {
|
|||||||
// savedExternalIds is null when we don't run SSH tests and the assume in
|
// savedExternalIds is null when we don't run SSH tests and the assume in
|
||||||
// @Before in AbstractDaemonTest prevents this class' @Before method from
|
// @Before in AbstractDaemonTest prevents this class' @Before method from
|
||||||
// being executed.
|
// being executed.
|
||||||
externalIdsUpdate.delete(getExternalIds(admin));
|
externalIdsUpdate.delete(externalIds.byAccount(admin.id));
|
||||||
externalIdsUpdate.delete(getExternalIds(user));
|
externalIdsUpdate.delete(externalIds.byAccount(user.id));
|
||||||
externalIdsUpdate.insert(savedExternalIds);
|
externalIdsUpdate.insert(savedExternalIds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -190,10 +193,6 @@ public class AccountIT extends AbstractDaemonTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<ExternalId> getExternalIds(TestAccount account) throws Exception {
|
|
||||||
return accountCache.get(account.getId()).getExternalIds();
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void deleteGpgKeys() throws Exception {
|
public void deleteGpgKeys() throws Exception {
|
||||||
String ref = REFS_GPG_KEYS;
|
String ref = REFS_GPG_KEYS;
|
||||||
@ -220,6 +219,67 @@ public class AccountIT extends AbstractDaemonTest {
|
|||||||
create(3); // account creation + external ID creation + adding SSH keys
|
create(3); // account creation + external ID creation + adding SSH keys
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void create(int expectedAccountReindexCalls) throws Exception {
|
||||||
|
String name = "foo";
|
||||||
|
TestAccount foo = accountCreator.create(name);
|
||||||
|
AccountInfo info = gApi.accounts().id(foo.id.get()).get();
|
||||||
|
assertThat(info.username).isEqualTo(name);
|
||||||
|
assertThat(info.name).isEqualTo(name);
|
||||||
|
accountIndexedCounter.assertReindexOf(foo, expectedAccountReindexCalls);
|
||||||
|
|
||||||
|
// check user branch
|
||||||
|
try (Repository repo = repoManager.openRepository(allUsers);
|
||||||
|
RevWalk rw = new RevWalk(repo);
|
||||||
|
ObjectReader or = repo.newObjectReader()) {
|
||||||
|
Ref ref = repo.exactRef(RefNames.refsUsers(foo.getId()));
|
||||||
|
assertThat(ref).isNotNull();
|
||||||
|
RevCommit c = rw.parseCommit(ref.getObjectId());
|
||||||
|
long timestampDiffMs =
|
||||||
|
Math.abs(
|
||||||
|
c.getCommitTime() * 1000L
|
||||||
|
- accountCache.get(foo.getId()).getAccount().getRegisteredOn().getTime());
|
||||||
|
assertThat(timestampDiffMs).isAtMost(ChangeRebuilderImpl.MAX_WINDOW_MS);
|
||||||
|
|
||||||
|
// Check the 'account.config' file.
|
||||||
|
try (TreeWalk tw = TreeWalk.forPath(or, AccountConfig.ACCOUNT_CONFIG, c.getTree())) {
|
||||||
|
assertThat(tw).isNotNull();
|
||||||
|
Config cfg = new Config();
|
||||||
|
cfg.fromText(new String(or.open(tw.getObjectId(0), OBJ_BLOB).getBytes(), UTF_8));
|
||||||
|
assertThat(cfg.getString(AccountConfig.ACCOUNT, null, AccountConfig.KEY_FULL_NAME))
|
||||||
|
.isEqualTo(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createAnonymousCoward() throws Exception {
|
||||||
|
TestAccount anonymousCoward = accountCreator.create();
|
||||||
|
accountIndexedCounter.assertReindexOf(anonymousCoward);
|
||||||
|
|
||||||
|
// check user branch
|
||||||
|
try (Repository repo = repoManager.openRepository(allUsers);
|
||||||
|
RevWalk rw = new RevWalk(repo);
|
||||||
|
ObjectReader or = repo.newObjectReader()) {
|
||||||
|
Ref ref = repo.exactRef(RefNames.refsUsers(anonymousCoward.getId()));
|
||||||
|
assertThat(ref).isNotNull();
|
||||||
|
RevCommit c = rw.parseCommit(ref.getObjectId());
|
||||||
|
long timestampDiffMs =
|
||||||
|
Math.abs(
|
||||||
|
c.getCommitTime() * 1000L
|
||||||
|
- accountCache
|
||||||
|
.get(anonymousCoward.getId())
|
||||||
|
.getAccount()
|
||||||
|
.getRegisteredOn()
|
||||||
|
.getTime());
|
||||||
|
assertThat(timestampDiffMs).isAtMost(ChangeRebuilderImpl.MAX_WINDOW_MS);
|
||||||
|
|
||||||
|
// No account properties were set, hence an 'account.config' file was not created.
|
||||||
|
try (TreeWalk tw = TreeWalk.forPath(or, AccountConfig.ACCOUNT_CONFIG, c.getTree())) {
|
||||||
|
assertThat(tw).isNull();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void get() throws Exception {
|
public void get() throws Exception {
|
||||||
AccountInfo info = gApi.accounts().id("admin").get();
|
AccountInfo info = gApi.accounts().id("admin").get();
|
||||||
@ -693,6 +753,36 @@ public class AccountIT extends AbstractDaemonTest {
|
|||||||
accountIndexedCounter.assertReindexOf(admin);
|
accountIndexedCounter.assertReindexOf(admin);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void pushAccountConfigToUserBranchForReviewIsRejectedOnSubmit() throws Exception {
|
||||||
|
String userRefName = RefNames.refsUsers(admin.id);
|
||||||
|
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
|
||||||
|
fetch(allUsersRepo, userRefName + ":userRef");
|
||||||
|
allUsersRepo.reset("userRef");
|
||||||
|
|
||||||
|
Config ac = getAccountConfig(allUsersRepo);
|
||||||
|
ac.setString(AccountConfig.ACCOUNT, null, AccountConfig.KEY_STATUS, "OOO");
|
||||||
|
|
||||||
|
PushOneCommit.Result r =
|
||||||
|
pushFactory
|
||||||
|
.create(
|
||||||
|
db,
|
||||||
|
admin.getIdent(),
|
||||||
|
allUsersRepo,
|
||||||
|
"Update account config",
|
||||||
|
AccountConfig.ACCOUNT_CONFIG,
|
||||||
|
ac.toText())
|
||||||
|
.to(MagicBranch.NEW_CHANGE + userRefName);
|
||||||
|
r.assertOkStatus();
|
||||||
|
accountIndexedCounter.assertNoReindex();
|
||||||
|
assertThat(r.getChange().change().getDest().get()).isEqualTo(userRefName);
|
||||||
|
gApi.changes().id(r.getChangeId()).current().review(ReviewInput.approve());
|
||||||
|
exception.expect(ResourceConflictException.class);
|
||||||
|
exception.expectMessage(
|
||||||
|
String.format("update of %s not allowed", AccountConfig.ACCOUNT_CONFIG));
|
||||||
|
gApi.changes().id(r.getChangeId()).current().submit();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void pushWatchConfigToUserBranch() throws Exception {
|
public void pushWatchConfigToUserBranch() throws Exception {
|
||||||
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
|
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
|
||||||
@ -734,6 +824,28 @@ public class AccountIT extends AbstractDaemonTest {
|
|||||||
WatchConfig.WATCH_CONFIG, admin.getId().get(), project.get(), invalidNotifyValue));
|
WatchConfig.WATCH_CONFIG, admin.getId().get(), project.get(), invalidNotifyValue));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void pushAccountConfigToUserBranchIsRejected() throws Exception {
|
||||||
|
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
|
||||||
|
fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
|
||||||
|
allUsersRepo.reset("userRef");
|
||||||
|
|
||||||
|
Config ac = getAccountConfig(allUsersRepo);
|
||||||
|
ac.setString(AccountConfig.ACCOUNT, null, AccountConfig.KEY_STATUS, "OOO");
|
||||||
|
|
||||||
|
PushOneCommit.Result r =
|
||||||
|
pushFactory
|
||||||
|
.create(
|
||||||
|
db,
|
||||||
|
admin.getIdent(),
|
||||||
|
allUsersRepo,
|
||||||
|
"Update account config",
|
||||||
|
AccountConfig.ACCOUNT_CONFIG,
|
||||||
|
ac.toText())
|
||||||
|
.to(RefNames.REFS_USERS_SELF);
|
||||||
|
r.assertErrorStatus("account update not allowed");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Sandboxed
|
@Sandboxed
|
||||||
public void cannotCreateUserBranch() throws Exception {
|
public void cannotCreateUserBranch() throws Exception {
|
||||||
@ -1071,26 +1183,6 @@ public class AccountIT extends AbstractDaemonTest {
|
|||||||
assertThat(checkInfo.checkAccountsResult.problems).containsExactlyElementsIn(expectedProblems);
|
assertThat(checkInfo.checkAccountsResult.problems).containsExactlyElementsIn(expectedProblems);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void create(int expectedAccountReindexCalls) throws Exception {
|
|
||||||
TestAccount foo = accountCreator.create("foo");
|
|
||||||
AccountInfo info = gApi.accounts().id(foo.id.get()).get();
|
|
||||||
assertThat(info.username).isEqualTo("foo");
|
|
||||||
accountIndexedCounter.assertReindexOf(foo, expectedAccountReindexCalls);
|
|
||||||
|
|
||||||
// check user branch
|
|
||||||
try (Repository repo = repoManager.openRepository(allUsers);
|
|
||||||
RevWalk rw = new RevWalk(repo)) {
|
|
||||||
Ref ref = repo.exactRef(RefNames.refsUsers(foo.getId()));
|
|
||||||
assertThat(ref).isNotNull();
|
|
||||||
RevCommit c = rw.parseCommit(ref.getObjectId());
|
|
||||||
long timestampDiffMs =
|
|
||||||
Math.abs(
|
|
||||||
c.getCommitTime() * 1000L
|
|
||||||
- accountCache.get(foo.getId()).getAccount().getRegisteredOn().getTime());
|
|
||||||
assertThat(timestampDiffMs).isAtMost(ChangeRebuilderImpl.MAX_WINDOW_MS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void assertSequenceNumbers(List<SshKeyInfo> sshKeys) {
|
private void assertSequenceNumbers(List<SshKeyInfo> sshKeys) {
|
||||||
int seq = 1;
|
int seq = 1;
|
||||||
for (SshKeyInfo key : sshKeys) {
|
for (SshKeyInfo key : sshKeys) {
|
||||||
@ -1212,6 +1304,26 @@ public class AccountIT extends AbstractDaemonTest {
|
|||||||
assertThat(Iterables.getOnlyElement(accounts)).isEqualTo(expectedAccount.getId());
|
assertThat(Iterables.getOnlyElement(accounts)).isEqualTo(expectedAccount.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Config getAccountConfig(TestRepository<?> allUsersRepo) throws Exception {
|
||||||
|
Config ac = new Config();
|
||||||
|
try (TreeWalk tw =
|
||||||
|
TreeWalk.forPath(
|
||||||
|
allUsersRepo.getRepository(),
|
||||||
|
AccountConfig.ACCOUNT_CONFIG,
|
||||||
|
getHead(allUsersRepo.getRepository()).getTree())) {
|
||||||
|
assertThat(tw).isNotNull();
|
||||||
|
ac.fromText(
|
||||||
|
new String(
|
||||||
|
allUsersRepo
|
||||||
|
.getRevWalk()
|
||||||
|
.getObjectReader()
|
||||||
|
.open(tw.getObjectId(0), OBJ_BLOB)
|
||||||
|
.getBytes(),
|
||||||
|
UTF_8));
|
||||||
|
}
|
||||||
|
return ac;
|
||||||
|
}
|
||||||
|
|
||||||
private static class AccountIndexedCounter implements AccountIndexedListener {
|
private static class AccountIndexedCounter implements AccountIndexedListener {
|
||||||
private final AtomicLongMap<Integer> countsByAccount = AtomicLongMap.create();
|
private final AtomicLongMap<Integer> countsByAccount = AtomicLongMap.create();
|
||||||
|
|
||||||
|
@ -37,7 +37,6 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
|
|||||||
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.AccountManager;
|
import com.google.gerrit.server.account.AccountManager;
|
||||||
import com.google.gerrit.server.account.Accounts;
|
|
||||||
import com.google.gerrit.server.account.AccountsUpdate;
|
import com.google.gerrit.server.account.AccountsUpdate;
|
||||||
import com.google.gerrit.server.account.AuthRequest;
|
import com.google.gerrit.server.account.AuthRequest;
|
||||||
import com.google.gerrit.server.account.externalids.ExternalId;
|
import com.google.gerrit.server.account.externalids.ExternalId;
|
||||||
@ -71,8 +70,6 @@ import org.junit.Test;
|
|||||||
|
|
||||||
/** Unit tests for {@link GerritPublicKeyChecker}. */
|
/** Unit tests for {@link GerritPublicKeyChecker}. */
|
||||||
public class GerritPublicKeyCheckerTest {
|
public class GerritPublicKeyCheckerTest {
|
||||||
@Inject private Accounts accounts;
|
|
||||||
|
|
||||||
@Inject private AccountsUpdate.Server accountsUpdate;
|
@Inject private AccountsUpdate.Server accountsUpdate;
|
||||||
|
|
||||||
@Inject private AccountManager accountManager;
|
@Inject private AccountManager accountManager;
|
||||||
@ -117,10 +114,8 @@ public class GerritPublicKeyCheckerTest {
|
|||||||
db = schemaFactory.open();
|
db = schemaFactory.open();
|
||||||
schemaCreator.create(db);
|
schemaCreator.create(db);
|
||||||
userId = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
|
userId = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
|
||||||
Account userAccount = accounts.get(db, userId);
|
|
||||||
// Note: does not match any key in TestKeys.
|
// Note: does not match any key in TestKeys.
|
||||||
userAccount.setPreferredEmail("user@example.com");
|
accountsUpdate.create().update(db, userId, a -> a.setPreferredEmail("user@example.com"));
|
||||||
accountsUpdate.create().update(db, userAccount);
|
|
||||||
user = reloadUser();
|
user = reloadUser();
|
||||||
|
|
||||||
requestContext.setContext(
|
requestContext.setContext(
|
||||||
|
@ -42,6 +42,7 @@ import javax.servlet.ServletRequest;
|
|||||||
import javax.servlet.ServletResponse;
|
import javax.servlet.ServletResponse;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
@ -111,7 +112,7 @@ class RunAsFilter implements Filter {
|
|||||||
Account target;
|
Account target;
|
||||||
try {
|
try {
|
||||||
target = accountResolver.find(db.get(), runas);
|
target = accountResolver.find(db.get(), runas);
|
||||||
} catch (OrmException e) {
|
} catch (OrmException | IOException | ConfigInvalidException e) {
|
||||||
log.warn("cannot resolve account for " + RUN_AS, e);
|
log.warn("cannot resolve account for " + RUN_AS, e);
|
||||||
replyError(req, res, SC_INTERNAL_SERVER_ERROR, "cannot resolve " + RUN_AS, e);
|
replyError(req, res, SC_INTERNAL_SERVER_ERROR, "cannot resolve " + RUN_AS, e);
|
||||||
return;
|
return;
|
||||||
|
@ -49,6 +49,7 @@ import javax.servlet.ServletException;
|
|||||||
import javax.servlet.http.HttpServlet;
|
import javax.servlet.http.HttpServlet;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.w3c.dom.Document;
|
import org.w3c.dom.Document;
|
||||||
import org.w3c.dom.Element;
|
import org.w3c.dom.Element;
|
||||||
|
|
||||||
@ -231,7 +232,7 @@ class BecomeAnyAccountLoginServlet extends HttpServlet {
|
|||||||
}
|
}
|
||||||
try (ReviewDb db = schema.open()) {
|
try (ReviewDb db = schema.open()) {
|
||||||
return auth(accounts.get(db, id));
|
return auth(accounts.get(db, id));
|
||||||
} catch (OrmException e) {
|
} catch (OrmException | IOException | ConfigInvalidException e) {
|
||||||
getServletContext().log("cannot query database", e);
|
getServletContext().log("cannot query database", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,13 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.gerrit.pgm.init.api.InitFlags;
|
import com.google.gerrit.pgm.init.api.InitFlags;
|
||||||
import com.google.gerrit.reviewdb.client.Account;
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
|
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.GerritPersonIdentProvider;
|
import com.google.gerrit.server.GerritPersonIdentProvider;
|
||||||
import com.google.gerrit.server.account.Accounts;
|
import com.google.gerrit.server.account.Accounts;
|
||||||
import com.google.gerrit.server.account.AccountsUpdate;
|
import com.google.gerrit.server.account.AccountsUpdate;
|
||||||
import com.google.gerrit.server.config.SitePaths;
|
import com.google.gerrit.server.config.SitePaths;
|
||||||
|
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -57,7 +59,15 @@ public class AccountsOnInit {
|
|||||||
ObjectInserter oi = repo.newObjectInserter()) {
|
ObjectInserter oi = repo.newObjectInserter()) {
|
||||||
PersonIdent serverIdent = new GerritPersonIdentProvider(flags.cfg).get();
|
PersonIdent serverIdent = new GerritPersonIdentProvider(flags.cfg).get();
|
||||||
AccountsUpdate.createUserBranch(
|
AccountsUpdate.createUserBranch(
|
||||||
repo, oi, serverIdent, serverIdent, account.getId(), account.getRegisteredOn());
|
repo,
|
||||||
|
new Project.NameKey(allUsers),
|
||||||
|
GitReferenceUpdated.DISABLED,
|
||||||
|
null,
|
||||||
|
oi,
|
||||||
|
serverIdent,
|
||||||
|
serverIdent,
|
||||||
|
account.getId(),
|
||||||
|
account.getRegisteredOn());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ import com.google.gerrit.server.query.change.InternalChangeQuery;
|
|||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
@ -60,6 +61,7 @@ import java.util.concurrent.Future;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import org.apache.commons.lang.mutable.MutableDouble;
|
import org.apache.commons.lang.mutable.MutableDouble;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -73,7 +75,7 @@ public class ReviewerRecommender {
|
|||||||
new double[] {
|
new double[] {
|
||||||
BASE_REVIEWER_WEIGHT, BASE_OWNER_WEIGHT, BASE_COMMENT_WEIGHT,
|
BASE_REVIEWER_WEIGHT, BASE_OWNER_WEIGHT, BASE_COMMENT_WEIGHT,
|
||||||
};
|
};
|
||||||
private static final long PLUGIN_QUERY_TIMEOUT = 500; //ms
|
private static final long PLUGIN_QUERY_TIMEOUT = 500; // ms
|
||||||
|
|
||||||
private final ChangeQueryBuilder changeQueryBuilder;
|
private final ChangeQueryBuilder changeQueryBuilder;
|
||||||
private final Config config;
|
private final Config config;
|
||||||
@ -108,7 +110,7 @@ public class ReviewerRecommender {
|
|||||||
SuggestReviewers suggestReviewers,
|
SuggestReviewers suggestReviewers,
|
||||||
ProjectControl projectControl,
|
ProjectControl projectControl,
|
||||||
List<Account.Id> candidateList)
|
List<Account.Id> candidateList)
|
||||||
throws OrmException {
|
throws OrmException, IOException, ConfigInvalidException {
|
||||||
String query = suggestReviewers.getQuery();
|
String query = suggestReviewers.getQuery();
|
||||||
double baseWeight = config.getInt("addReviewer", "baseWeight", 1);
|
double baseWeight = config.getInt("addReviewer", "baseWeight", 1);
|
||||||
|
|
||||||
@ -196,7 +198,7 @@ public class ReviewerRecommender {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Map<Account.Id, MutableDouble> baseRankingForEmptyQuery(double baseWeight)
|
private Map<Account.Id, MutableDouble> baseRankingForEmptyQuery(double baseWeight)
|
||||||
throws OrmException {
|
throws OrmException, IOException, ConfigInvalidException {
|
||||||
// Get the user's last 25 changes, check approvals
|
// Get the user's last 25 changes, check approvals
|
||||||
try {
|
try {
|
||||||
List<ChangeData> result =
|
List<ChangeData> result =
|
||||||
@ -225,7 +227,7 @@ public class ReviewerRecommender {
|
|||||||
|
|
||||||
private Map<Account.Id, MutableDouble> baseRankingForCandidateList(
|
private Map<Account.Id, MutableDouble> baseRankingForCandidateList(
|
||||||
List<Account.Id> candidates, ProjectControl projectControl, double baseWeight)
|
List<Account.Id> candidates, ProjectControl projectControl, double baseWeight)
|
||||||
throws OrmException {
|
throws OrmException, IOException, ConfigInvalidException {
|
||||||
// Get each reviewer's activity based on number of applied labels
|
// Get each reviewer's activity based on number of applied labels
|
||||||
// (weighted 10d), number of comments (weighted 0.5d) and number of owned
|
// (weighted 10d), number of comments (weighted 0.5d) and number of owned
|
||||||
// changes (weighted 1d).
|
// changes (weighted 1d).
|
||||||
|
@ -56,6 +56,7 @@ import java.util.EnumSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
public class ReviewersUtil {
|
public class ReviewersUtil {
|
||||||
@Singleton
|
@Singleton
|
||||||
@ -146,7 +147,7 @@ public class ReviewersUtil {
|
|||||||
ProjectControl projectControl,
|
ProjectControl projectControl,
|
||||||
VisibilityControl visibilityControl,
|
VisibilityControl visibilityControl,
|
||||||
boolean excludeGroups)
|
boolean excludeGroups)
|
||||||
throws IOException, OrmException {
|
throws IOException, OrmException, ConfigInvalidException {
|
||||||
String query = suggestReviewers.getQuery();
|
String query = suggestReviewers.getQuery();
|
||||||
int limit = suggestReviewers.getLimit();
|
int limit = suggestReviewers.getLimit();
|
||||||
|
|
||||||
@ -212,7 +213,7 @@ public class ReviewersUtil {
|
|||||||
SuggestReviewers suggestReviewers,
|
SuggestReviewers suggestReviewers,
|
||||||
ProjectControl projectControl,
|
ProjectControl projectControl,
|
||||||
List<Account.Id> candidateList)
|
List<Account.Id> candidateList)
|
||||||
throws OrmException {
|
throws OrmException, IOException, ConfigInvalidException {
|
||||||
try (Timer0.Context ctx = metrics.recommendAccountsLatency.start()) {
|
try (Timer0.Context ctx = metrics.recommendAccountsLatency.start()) {
|
||||||
return reviewerRecommender.suggestReviewers(
|
return reviewerRecommender.suggestReviewers(
|
||||||
changeNotes, suggestReviewers, projectControl, candidateList);
|
changeNotes, suggestReviewers, projectControl, candidateList);
|
||||||
|
@ -0,0 +1,260 @@
|
|||||||
|
// Copyright (C) 2017 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;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.gerrit.common.Nullable;
|
||||||
|
import com.google.gerrit.common.TimeUtil;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
|
import com.google.gerrit.server.git.ValidationError;
|
||||||
|
import com.google.gerrit.server.git.VersionedMetaData;
|
||||||
|
import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
|
||||||
|
import com.google.gwtorm.server.OrmDuplicateKeyException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
import org.eclipse.jgit.lib.CommitBuilder;
|
||||||
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
|
import org.eclipse.jgit.revwalk.RevSort;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ‘account.config’ file in the user branch in the All-Users repository that contains the properties
|
||||||
|
* of the account.
|
||||||
|
*
|
||||||
|
* <p>The 'account.config' file is a git config file that has one 'account' section with the
|
||||||
|
* properties of the account:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* [account]
|
||||||
|
* active = false
|
||||||
|
* fullName = John Doe
|
||||||
|
* preferredEmail = john.doe@foo.com
|
||||||
|
* status = Overloaded with reviews
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* <p>All keys are optional. This means 'account.config' may not exist on the user branch if no
|
||||||
|
* properties are set.
|
||||||
|
*
|
||||||
|
* <p>Not setting a key and setting a key to an empty string are treated the same way and result in
|
||||||
|
* a {@code null} value.
|
||||||
|
*
|
||||||
|
* <p>If no value for 'active' is specified, by default the account is considered as active.
|
||||||
|
*
|
||||||
|
* <p>The commit date of the first commit on the user branch is used as registration date of the
|
||||||
|
* account. The first commit may be an empty commit (if no properties were set and 'account.config'
|
||||||
|
* doesn't exist).
|
||||||
|
*/
|
||||||
|
public class AccountConfig extends VersionedMetaData implements ValidationError.Sink {
|
||||||
|
public static final String ACCOUNT_CONFIG = "account.config";
|
||||||
|
public static final String ACCOUNT = "account";
|
||||||
|
public static final String KEY_ACTIVE = "active";
|
||||||
|
public static final String KEY_FULL_NAME = "fullName";
|
||||||
|
public static final String KEY_PREFERRED_EMAIL = "preferredEmail";
|
||||||
|
public static final String KEY_STATUS = "status";
|
||||||
|
|
||||||
|
@Nullable private final OutgoingEmailValidator emailValidator;
|
||||||
|
private final Account.Id accountId;
|
||||||
|
private final String ref;
|
||||||
|
|
||||||
|
private boolean isLoaded;
|
||||||
|
private Account account;
|
||||||
|
private Timestamp registeredOn;
|
||||||
|
private List<ValidationError> validationErrors;
|
||||||
|
|
||||||
|
public AccountConfig(@Nullable OutgoingEmailValidator emailValidator, Account.Id accountId) {
|
||||||
|
this.emailValidator = emailValidator;
|
||||||
|
this.accountId = accountId;
|
||||||
|
this.ref = RefNames.refsUsers(accountId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getRefName() {
|
||||||
|
return ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the loaded account.
|
||||||
|
*
|
||||||
|
* @return loaded account.
|
||||||
|
* @throws IllegalStateException if the account was not loaded yet
|
||||||
|
*/
|
||||||
|
public Account getAccount() {
|
||||||
|
checkLoaded();
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the account. This means the loaded account will be overwritten with the given account.
|
||||||
|
*
|
||||||
|
* <p>Changing the registration date of an account is not supported.
|
||||||
|
*
|
||||||
|
* @param account account that should be set
|
||||||
|
* @throws IllegalStateException if the account was not loaded yet
|
||||||
|
*/
|
||||||
|
public void setAccount(Account account) {
|
||||||
|
checkLoaded();
|
||||||
|
this.account = account;
|
||||||
|
this.registeredOn = account.getRegisteredOn();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new account.
|
||||||
|
*
|
||||||
|
* @return the new account
|
||||||
|
* @throws OrmDuplicateKeyException if the user branch already exists
|
||||||
|
*/
|
||||||
|
public Account getNewAccount() throws OrmDuplicateKeyException {
|
||||||
|
checkLoaded();
|
||||||
|
if (revision != null) {
|
||||||
|
throw new OrmDuplicateKeyException(String.format("account %s already exists", accountId));
|
||||||
|
}
|
||||||
|
this.registeredOn = TimeUtil.nowTs();
|
||||||
|
this.account = new Account(accountId, registeredOn);
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLoad() throws IOException, ConfigInvalidException {
|
||||||
|
if (revision != null) {
|
||||||
|
rw.markStart(revision);
|
||||||
|
rw.sort(RevSort.REVERSE);
|
||||||
|
registeredOn = new Timestamp(rw.next().getCommitTime() * 1000L);
|
||||||
|
|
||||||
|
Config cfg = readConfig(ACCOUNT_CONFIG);
|
||||||
|
|
||||||
|
account = parse(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoaded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Account parse(Config cfg) {
|
||||||
|
Account account = new Account(accountId, registeredOn);
|
||||||
|
account.setActive(cfg.getBoolean(ACCOUNT, null, KEY_ACTIVE, true));
|
||||||
|
account.setFullName(get(cfg, KEY_FULL_NAME));
|
||||||
|
|
||||||
|
String preferredEmail = get(cfg, KEY_PREFERRED_EMAIL);
|
||||||
|
account.setPreferredEmail(preferredEmail);
|
||||||
|
if (emailValidator != null && !emailValidator.isValid(preferredEmail)) {
|
||||||
|
error(
|
||||||
|
new ValidationError(
|
||||||
|
ACCOUNT_CONFIG, String.format("Invalid preferred email: %s", preferredEmail)));
|
||||||
|
}
|
||||||
|
|
||||||
|
account.setStatus(get(cfg, KEY_STATUS));
|
||||||
|
return account;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean onSave(CommitBuilder commit) throws IOException, ConfigInvalidException {
|
||||||
|
checkLoaded();
|
||||||
|
|
||||||
|
if (revision != null) {
|
||||||
|
commit.setMessage("Update account\n");
|
||||||
|
} else if (account != null) {
|
||||||
|
commit.setMessage("Create account\n");
|
||||||
|
commit.setAuthor(new PersonIdent(commit.getAuthor(), registeredOn));
|
||||||
|
commit.setCommitter(new PersonIdent(commit.getCommitter(), registeredOn));
|
||||||
|
}
|
||||||
|
|
||||||
|
Config cfg = readConfig(ACCOUNT_CONFIG);
|
||||||
|
setActive(cfg, account.isActive());
|
||||||
|
set(cfg, KEY_FULL_NAME, account.getFullName());
|
||||||
|
set(cfg, KEY_PREFERRED_EMAIL, account.getPreferredEmail());
|
||||||
|
set(cfg, KEY_STATUS, account.getStatus());
|
||||||
|
saveConfig(ACCOUNT_CONFIG, cfg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets/Unsets {@code account.active} in the given config.
|
||||||
|
*
|
||||||
|
* <p>{@code account.active} is set to {@code false} if the account is inactive.
|
||||||
|
*
|
||||||
|
* <p>If the account is active {@code account.active} is unset since {@code true} is the default
|
||||||
|
* if this field is missing.
|
||||||
|
*
|
||||||
|
* @param cfg the config
|
||||||
|
* @param value whether the account is active
|
||||||
|
*/
|
||||||
|
private static void setActive(Config cfg, boolean value) {
|
||||||
|
if (!value) {
|
||||||
|
cfg.setBoolean(ACCOUNT, null, KEY_ACTIVE, false);
|
||||||
|
} else {
|
||||||
|
cfg.unset(ACCOUNT, null, KEY_ACTIVE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets/Unsets the given key in the given config.
|
||||||
|
*
|
||||||
|
* <p>The key unset if the value is {@code null}.
|
||||||
|
*
|
||||||
|
* @param cfg the config
|
||||||
|
* @param key the key
|
||||||
|
* @param value the value
|
||||||
|
*/
|
||||||
|
private static void set(Config cfg, String key, String value) {
|
||||||
|
if (!Strings.isNullOrEmpty(value)) {
|
||||||
|
cfg.setString(ACCOUNT, null, key, value);
|
||||||
|
} else {
|
||||||
|
cfg.unset(ACCOUNT, null, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the given key from the given config.
|
||||||
|
*
|
||||||
|
* <p>Empty values are returned as {@code null}
|
||||||
|
*
|
||||||
|
* @param cfg the config
|
||||||
|
* @param key the key
|
||||||
|
* @return the value, {@code null} if key was not set or key was set to empty string
|
||||||
|
*/
|
||||||
|
private static String get(Config cfg, String key) {
|
||||||
|
return Strings.emptyToNull(cfg.getString(ACCOUNT, null, key));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkLoaded() {
|
||||||
|
checkState(isLoaded, "account not loaded yet");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation errors, if any were discovered during load.
|
||||||
|
*
|
||||||
|
* @return list of errors; empty list if there are no errors.
|
||||||
|
*/
|
||||||
|
public List<ValidationError> getValidationErrors() {
|
||||||
|
if (validationErrors != null) {
|
||||||
|
return ImmutableList.copyOf(validationErrors);
|
||||||
|
}
|
||||||
|
return ImmutableList.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(ValidationError error) {
|
||||||
|
if (validationErrors == null) {
|
||||||
|
validationErrors = new ArrayList<>(4);
|
||||||
|
}
|
||||||
|
validationErrors.add(error);
|
||||||
|
}
|
||||||
|
}
|
@ -16,7 +16,6 @@ package com.google.gerrit.server.account;
|
|||||||
|
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.gerrit.audit.AuditService;
|
import com.google.gerrit.audit.AuditService;
|
||||||
import com.google.gerrit.common.TimeUtil;
|
|
||||||
import com.google.gerrit.common.data.AccessSection;
|
import com.google.gerrit.common.data.AccessSection;
|
||||||
import com.google.gerrit.common.data.GlobalCapability;
|
import com.google.gerrit.common.data.GlobalCapability;
|
||||||
import com.google.gerrit.common.data.Permission;
|
import com.google.gerrit.common.data.Permission;
|
||||||
@ -39,10 +38,13 @@ import com.google.inject.Inject;
|
|||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -150,7 +152,7 @@ public class AccountManager {
|
|||||||
private void update(ReviewDb db, AuthRequest who, ExternalId extId)
|
private void update(ReviewDb db, AuthRequest who, ExternalId extId)
|
||||||
throws OrmException, IOException, ConfigInvalidException {
|
throws OrmException, IOException, ConfigInvalidException {
|
||||||
IdentifiedUser user = userFactory.create(extId.accountId());
|
IdentifiedUser user = userFactory.create(extId.accountId());
|
||||||
Account toUpdate = null;
|
List<Consumer<Account>> accountUpdates = new ArrayList<>();
|
||||||
|
|
||||||
// If the email address was modified by the authentication provider,
|
// If the email address was modified by the authentication provider,
|
||||||
// update our records to match the changed email.
|
// update our records to match the changed email.
|
||||||
@ -159,8 +161,7 @@ public class AccountManager {
|
|||||||
String oldEmail = extId.email();
|
String oldEmail = extId.email();
|
||||||
if (newEmail != null && !newEmail.equals(oldEmail)) {
|
if (newEmail != null && !newEmail.equals(oldEmail)) {
|
||||||
if (oldEmail != null && oldEmail.equals(user.getAccount().getPreferredEmail())) {
|
if (oldEmail != null && oldEmail.equals(user.getAccount().getPreferredEmail())) {
|
||||||
toUpdate = load(toUpdate, user.getAccountId(), db);
|
accountUpdates.add(a -> a.setPreferredEmail(newEmail));
|
||||||
toUpdate.setPreferredEmail(newEmail);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
externalIdsUpdateFactory
|
externalIdsUpdateFactory
|
||||||
@ -172,8 +173,7 @@ public class AccountManager {
|
|||||||
if (!realm.allowsEdit(AccountFieldName.FULL_NAME)
|
if (!realm.allowsEdit(AccountFieldName.FULL_NAME)
|
||||||
&& !Strings.isNullOrEmpty(who.getDisplayName())
|
&& !Strings.isNullOrEmpty(who.getDisplayName())
|
||||||
&& !eq(user.getAccount().getFullName(), who.getDisplayName())) {
|
&& !eq(user.getAccount().getFullName(), who.getDisplayName())) {
|
||||||
toUpdate = load(toUpdate, user.getAccountId(), db);
|
accountUpdates.add(a -> a.setFullName(who.getDisplayName()));
|
||||||
toUpdate.setFullName(who.getDisplayName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!realm.allowsEdit(AccountFieldName.USER_NAME)
|
if (!realm.allowsEdit(AccountFieldName.USER_NAME)
|
||||||
@ -184,8 +184,12 @@ public class AccountManager {
|
|||||||
"Not changing already set username %s to %s", user.getUserName(), who.getUserName()));
|
"Not changing already set username %s to %s", user.getUserName(), who.getUserName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (toUpdate != null) {
|
if (!accountUpdates.isEmpty()) {
|
||||||
accountsUpdateFactory.create().update(db, toUpdate);
|
Account account =
|
||||||
|
accountsUpdateFactory.create().update(db, user.getAccountId(), accountUpdates);
|
||||||
|
if (account == null) {
|
||||||
|
throw new OrmException("Account " + user.getAccountId() + " has been deleted");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newEmail != null && !newEmail.equals(oldEmail)) {
|
if (newEmail != null && !newEmail.equals(oldEmail)) {
|
||||||
@ -194,16 +198,6 @@ public class AccountManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Account load(Account toUpdate, Account.Id accountId, ReviewDb db) throws OrmException {
|
|
||||||
if (toUpdate == null) {
|
|
||||||
toUpdate = accounts.get(db, accountId);
|
|
||||||
if (toUpdate == null) {
|
|
||||||
throw new OrmException("Account " + accountId + " has been deleted");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return toUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean eq(String a, String b) {
|
private static boolean eq(String a, String b) {
|
||||||
return (a == null && b == null) || (a != null && a.equals(b));
|
return (a == null && b == null) || (a != null && a.equals(b));
|
||||||
}
|
}
|
||||||
@ -211,18 +205,23 @@ public class AccountManager {
|
|||||||
private AuthResult create(ReviewDb db, AuthRequest who)
|
private AuthResult create(ReviewDb db, AuthRequest who)
|
||||||
throws OrmException, AccountException, IOException, ConfigInvalidException {
|
throws OrmException, AccountException, IOException, ConfigInvalidException {
|
||||||
Account.Id newId = new Account.Id(db.nextAccountId());
|
Account.Id newId = new Account.Id(db.nextAccountId());
|
||||||
Account account = new Account(newId, TimeUtil.nowTs());
|
|
||||||
|
|
||||||
ExternalId extId =
|
ExternalId extId =
|
||||||
ExternalId.createWithEmail(who.getExternalIdKey(), newId, who.getEmailAddress());
|
ExternalId.createWithEmail(who.getExternalIdKey(), newId, who.getEmailAddress());
|
||||||
account.setFullName(who.getDisplayName());
|
|
||||||
account.setPreferredEmail(extId.email());
|
|
||||||
|
|
||||||
boolean isFirstAccount = awaitsFirstAccountCheck.getAndSet(false) && !accounts.hasAnyAccount();
|
boolean isFirstAccount = awaitsFirstAccountCheck.getAndSet(false) && !accounts.hasAnyAccount();
|
||||||
|
|
||||||
|
Account account;
|
||||||
try {
|
try {
|
||||||
AccountsUpdate accountsUpdate = accountsUpdateFactory.create();
|
AccountsUpdate accountsUpdate = accountsUpdateFactory.create();
|
||||||
accountsUpdate.upsert(db, account);
|
account =
|
||||||
|
accountsUpdate.insert(
|
||||||
|
db,
|
||||||
|
newId,
|
||||||
|
a -> {
|
||||||
|
a.setFullName(who.getDisplayName());
|
||||||
|
a.setPreferredEmail(extId.email());
|
||||||
|
});
|
||||||
|
|
||||||
ExternalId existingExtId = externalIds.get(extId.key());
|
ExternalId existingExtId = externalIds.get(extId.key());
|
||||||
if (existingExtId != null && !existingExtId.accountId().equals(extId.accountId())) {
|
if (existingExtId != null && !existingExtId.accountId().equals(extId.accountId())) {
|
||||||
@ -364,11 +363,16 @@ public class AccountManager {
|
|||||||
.insert(ExternalId.createWithEmail(who.getExternalIdKey(), to, who.getEmailAddress()));
|
.insert(ExternalId.createWithEmail(who.getExternalIdKey(), to, who.getEmailAddress()));
|
||||||
|
|
||||||
if (who.getEmailAddress() != null) {
|
if (who.getEmailAddress() != null) {
|
||||||
Account a = accounts.get(db, to);
|
accountsUpdateFactory
|
||||||
if (a.getPreferredEmail() == null) {
|
.create()
|
||||||
a.setPreferredEmail(who.getEmailAddress());
|
.update(
|
||||||
accountsUpdateFactory.create().update(db, a);
|
db,
|
||||||
}
|
to,
|
||||||
|
a -> {
|
||||||
|
if (a.getPreferredEmail() == null) {
|
||||||
|
a.setPreferredEmail(who.getEmailAddress());
|
||||||
|
}
|
||||||
|
});
|
||||||
byEmailCache.evict(who.getEmailAddress());
|
byEmailCache.evict(who.getEmailAddress());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -428,12 +432,17 @@ public class AccountManager {
|
|||||||
externalIdsUpdateFactory.create().delete(extId);
|
externalIdsUpdateFactory.create().delete(extId);
|
||||||
|
|
||||||
if (who.getEmailAddress() != null) {
|
if (who.getEmailAddress() != null) {
|
||||||
Account a = accounts.get(db, from);
|
accountsUpdateFactory
|
||||||
if (a.getPreferredEmail() != null
|
.create()
|
||||||
&& a.getPreferredEmail().equals(who.getEmailAddress())) {
|
.update(
|
||||||
a.setPreferredEmail(null);
|
db,
|
||||||
accountsUpdateFactory.create().update(db, a);
|
from,
|
||||||
}
|
a -> {
|
||||||
|
if (a.getPreferredEmail() != null
|
||||||
|
&& a.getPreferredEmail().equals(who.getEmailAddress())) {
|
||||||
|
a.setPreferredEmail(null);
|
||||||
|
}
|
||||||
|
});
|
||||||
byEmailCache.evict(who.getEmailAddress());
|
byEmailCache.evict(who.getEmailAddress());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,12 +23,14 @@ import com.google.gwtorm.server.OrmException;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
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.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;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class AccountResolver {
|
public class AccountResolver {
|
||||||
@ -61,7 +63,8 @@ public class AccountResolver {
|
|||||||
* @return the single account that matches; null if no account matches or there are multiple
|
* @return the single account that matches; null if no account matches or there are multiple
|
||||||
* candidates.
|
* candidates.
|
||||||
*/
|
*/
|
||||||
public Account find(ReviewDb db, String nameOrEmail) throws OrmException {
|
public Account find(ReviewDb db, String nameOrEmail)
|
||||||
|
throws OrmException, IOException, ConfigInvalidException {
|
||||||
Set<Account.Id> r = findAll(db, nameOrEmail);
|
Set<Account.Id> r = findAll(db, nameOrEmail);
|
||||||
if (r.size() == 1) {
|
if (r.size() == 1) {
|
||||||
return byId.get(r.iterator().next()).getAccount();
|
return byId.get(r.iterator().next()).getAccount();
|
||||||
@ -90,7 +93,8 @@ public class AccountResolver {
|
|||||||
* name ("username").
|
* name ("username").
|
||||||
* @return the accounts that match, empty collection if none. Never null.
|
* @return the accounts that match, empty collection if none. Never null.
|
||||||
*/
|
*/
|
||||||
public Set<Account.Id> findAll(ReviewDb db, String nameOrEmail) throws OrmException {
|
public Set<Account.Id> findAll(ReviewDb db, String nameOrEmail)
|
||||||
|
throws OrmException, IOException, ConfigInvalidException {
|
||||||
Matcher m = Pattern.compile("^.* \\(([1-9][0-9]*)\\)$").matcher(nameOrEmail);
|
Matcher m = Pattern.compile("^.* \\(([1-9][0-9]*)\\)$").matcher(nameOrEmail);
|
||||||
if (m.matches()) {
|
if (m.matches()) {
|
||||||
Account.Id id = Account.Id.parse(m.group(1));
|
Account.Id id = Account.Id.parse(m.group(1));
|
||||||
@ -118,7 +122,8 @@ public class AccountResolver {
|
|||||||
return findAllByNameOrEmail(db, nameOrEmail);
|
return findAllByNameOrEmail(db, nameOrEmail);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean exists(ReviewDb db, Account.Id id) throws OrmException {
|
private boolean exists(ReviewDb db, Account.Id id)
|
||||||
|
throws OrmException, IOException, ConfigInvalidException {
|
||||||
return accounts.get(db, id) != null;
|
return accounts.get(db, id) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,35 +22,70 @@ import com.google.gerrit.reviewdb.client.Account;
|
|||||||
import com.google.gerrit.reviewdb.client.RefNames;
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.config.AllUsersName;
|
import com.google.gerrit.server.config.AllUsersName;
|
||||||
|
import com.google.gerrit.server.config.GerritServerConfig;
|
||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
|
import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
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.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
/** Class to access accounts. */
|
/** Class to access accounts. */
|
||||||
@Singleton
|
@Singleton
|
||||||
public class Accounts {
|
public class Accounts {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(Accounts.class);
|
||||||
|
|
||||||
|
private final boolean readFromGit;
|
||||||
private final GitRepositoryManager repoManager;
|
private final GitRepositoryManager repoManager;
|
||||||
private final AllUsersName allUsersName;
|
private final AllUsersName allUsersName;
|
||||||
|
private final OutgoingEmailValidator emailValidator;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
Accounts(GitRepositoryManager repoManager, AllUsersName allUsersName) {
|
Accounts(
|
||||||
|
@GerritServerConfig Config cfg,
|
||||||
|
GitRepositoryManager repoManager,
|
||||||
|
AllUsersName allUsersName,
|
||||||
|
OutgoingEmailValidator emailValidator) {
|
||||||
|
this.readFromGit = cfg.getBoolean("user", null, "readAccountsFromGit", false);
|
||||||
this.repoManager = repoManager;
|
this.repoManager = repoManager;
|
||||||
this.allUsersName = allUsersName;
|
this.allUsersName = allUsersName;
|
||||||
|
this.emailValidator = emailValidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Account get(ReviewDb db, Account.Id accountId) throws OrmException {
|
public Account get(ReviewDb db, Account.Id accountId)
|
||||||
|
throws OrmException, IOException, ConfigInvalidException {
|
||||||
|
if (readFromGit) {
|
||||||
|
try (Repository repo = repoManager.openRepository(allUsersName)) {
|
||||||
|
return read(repo, accountId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return db.accounts().get(accountId);
|
return db.accounts().get(accountId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Account> get(ReviewDb db, Collection<Account.Id> accountIds) throws OrmException {
|
public List<Account> get(ReviewDb db, Collection<Account.Id> accountIds)
|
||||||
|
throws OrmException, IOException, ConfigInvalidException {
|
||||||
|
if (readFromGit) {
|
||||||
|
List<Account> accounts = new ArrayList<>(accountIds.size());
|
||||||
|
try (Repository repo = repoManager.openRepository(allUsersName)) {
|
||||||
|
for (Account.Id accountId : accountIds) {
|
||||||
|
accounts.add(read(repo, accountId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
|
||||||
return db.accounts().get(accountIds).toList();
|
return db.accounts().get(accountIds).toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +94,22 @@ public class Accounts {
|
|||||||
*
|
*
|
||||||
* @return all accounts
|
* @return all accounts
|
||||||
*/
|
*/
|
||||||
public List<Account> all(ReviewDb db) throws OrmException {
|
public List<Account> all(ReviewDb db) throws OrmException, IOException {
|
||||||
|
if (readFromGit) {
|
||||||
|
Set<Account.Id> accountIds = allIds();
|
||||||
|
List<Account> accounts = new ArrayList<>(accountIds.size());
|
||||||
|
try (Repository repo = repoManager.openRepository(allUsersName)) {
|
||||||
|
for (Account.Id accountId : accountIds) {
|
||||||
|
try {
|
||||||
|
accounts.add(read(repo, accountId));
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(String.format("Ignoring invalid account %s", accountId.get()), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return accounts;
|
||||||
|
}
|
||||||
|
|
||||||
return db.accounts().all().toList();
|
return db.accounts().all().toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,6 +153,13 @@ public class Accounts {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Account read(Repository allUsersRepository, Account.Id accountId)
|
||||||
|
throws IOException, ConfigInvalidException {
|
||||||
|
AccountConfig accountConfig = new AccountConfig(emailValidator, accountId);
|
||||||
|
accountConfig.load(allUsersRepository);
|
||||||
|
return accountConfig.getAccount();
|
||||||
|
}
|
||||||
|
|
||||||
private static Stream<Account.Id> readUserRefs(Repository repo) throws IOException {
|
private static Stream<Account.Id> readUserRefs(Repository repo) throws IOException {
|
||||||
return repo.getRefDatabase()
|
return repo.getRefDatabase()
|
||||||
.getRefs(RefNames.REFS_USERS)
|
.getRefs(RefNames.REFS_USERS)
|
||||||
|
@ -33,6 +33,8 @@ import com.google.gwtorm.server.OrmException;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class AccountsCollection
|
public class AccountsCollection
|
||||||
@ -68,7 +70,8 @@ public class AccountsCollection
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AccountResource parse(TopLevelResource root, IdString id)
|
public AccountResource parse(TopLevelResource root, IdString id)
|
||||||
throws ResourceNotFoundException, AuthException, OrmException {
|
throws ResourceNotFoundException, AuthException, OrmException, IOException,
|
||||||
|
ConfigInvalidException {
|
||||||
IdentifiedUser user = parseId(id.get());
|
IdentifiedUser user = parseId(id.get());
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new ResourceNotFoundException(id);
|
throw new ResourceNotFoundException(id);
|
||||||
@ -89,7 +92,8 @@ public class AccountsCollection
|
|||||||
* account is not visible to the calling user
|
* account is not visible to the calling user
|
||||||
*/
|
*/
|
||||||
public IdentifiedUser parse(String id)
|
public IdentifiedUser parse(String id)
|
||||||
throws AuthException, UnprocessableEntityException, OrmException {
|
throws AuthException, UnprocessableEntityException, OrmException, IOException,
|
||||||
|
ConfigInvalidException {
|
||||||
return parseOnBehalfOf(null, id);
|
return parseOnBehalfOf(null, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,8 +108,11 @@ public class AccountsCollection
|
|||||||
* @throws AuthException thrown if 'self' is used as account ID and the current user is not
|
* @throws AuthException thrown if 'self' is used as account ID and the current user is not
|
||||||
* authenticated
|
* authenticated
|
||||||
* @throws OrmException
|
* @throws OrmException
|
||||||
|
* @throws ConfigInvalidException
|
||||||
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
public IdentifiedUser parseId(String id) throws AuthException, OrmException {
|
public IdentifiedUser parseId(String id)
|
||||||
|
throws AuthException, OrmException, IOException, ConfigInvalidException {
|
||||||
return parseIdOnBehalfOf(null, id);
|
return parseIdOnBehalfOf(null, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +120,8 @@ public class AccountsCollection
|
|||||||
* Like {@link #parse(String)}, but also sets the {@link CurrentUser#getRealUser()} on the result.
|
* Like {@link #parse(String)}, but also sets the {@link CurrentUser#getRealUser()} on the result.
|
||||||
*/
|
*/
|
||||||
public IdentifiedUser parseOnBehalfOf(@Nullable CurrentUser caller, String id)
|
public IdentifiedUser parseOnBehalfOf(@Nullable CurrentUser caller, String id)
|
||||||
throws AuthException, UnprocessableEntityException, OrmException {
|
throws AuthException, UnprocessableEntityException, OrmException, IOException,
|
||||||
|
ConfigInvalidException {
|
||||||
IdentifiedUser user = parseIdOnBehalfOf(caller, id);
|
IdentifiedUser user = parseIdOnBehalfOf(caller, id);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw new UnprocessableEntityException(String.format("Account Not Found: %s", id));
|
throw new UnprocessableEntityException(String.format("Account Not Found: %s", id));
|
||||||
@ -124,7 +132,7 @@ public class AccountsCollection
|
|||||||
}
|
}
|
||||||
|
|
||||||
private IdentifiedUser parseIdOnBehalfOf(@Nullable CurrentUser caller, String id)
|
private IdentifiedUser parseIdOnBehalfOf(@Nullable CurrentUser caller, String id)
|
||||||
throws AuthException, OrmException {
|
throws AuthException, OrmException, IOException, ConfigInvalidException {
|
||||||
if (id.equals("self")) {
|
if (id.equals("self")) {
|
||||||
CurrentUser user = self.get();
|
CurrentUser user = self.get();
|
||||||
if (user.isIdentifiedUser()) {
|
if (user.isIdentifiedUser()) {
|
||||||
|
@ -16,15 +16,21 @@ package com.google.gerrit.server.account;
|
|||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
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 com.google.gerrit.reviewdb.client.Project;
|
||||||
import com.google.gerrit.reviewdb.client.RefNames;
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
import com.google.gerrit.reviewdb.server.ReviewDb;
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.GerritPersonIdent;
|
import com.google.gerrit.server.GerritPersonIdent;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.config.AllUsersName;
|
import com.google.gerrit.server.config.AllUsersName;
|
||||||
|
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
|
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||||
|
import com.google.gerrit.server.index.change.ReindexAfterRefUpdate;
|
||||||
|
import com.google.gerrit.server.mail.send.OutgoingEmailValidator;
|
||||||
import com.google.gwtorm.server.OrmDuplicateKeyException;
|
import com.google.gwtorm.server.OrmDuplicateKeyException;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
@ -32,7 +38,9 @@ import com.google.inject.Provider;
|
|||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
|
import java.util.List;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.lib.CommitBuilder;
|
import org.eclipse.jgit.lib.CommitBuilder;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
@ -46,8 +54,18 @@ import org.eclipse.jgit.lib.Repository;
|
|||||||
/**
|
/**
|
||||||
* Updates accounts.
|
* Updates accounts.
|
||||||
*
|
*
|
||||||
* <p>On updating accounts this class takes care to evict them from the account cache and thus
|
* <p>The account updates are written to both ReviewDb and NoteDb.
|
||||||
* triggers reindex for them.
|
*
|
||||||
|
* <p>In NoteDb accounts are represented as user branches in the All-Users repository. Optionally a
|
||||||
|
* user branch can contain a 'account.config' file that stores account properties, such as full
|
||||||
|
* name, preferred email, status and the active flag. The timestamp of the first commit on a user
|
||||||
|
* branch denotes the registration date. The initial commit on the user branch may be empty (since
|
||||||
|
* having an 'account.config' is optional). See {@link AccountConfig} for details of the
|
||||||
|
* 'account.config' file format.
|
||||||
|
*
|
||||||
|
* <p>On updating accounts the accounts are evicted from the account cache and thus reindexed. The
|
||||||
|
* eviction from the account cache is done by the {@link ReindexAfterRefUpdate} class which receives
|
||||||
|
* the event about updating the user branch that is triggered by this class.
|
||||||
*/
|
*/
|
||||||
@Singleton
|
@Singleton
|
||||||
public class AccountsUpdate {
|
public class AccountsUpdate {
|
||||||
@ -60,56 +78,38 @@ public class AccountsUpdate {
|
|||||||
@Singleton
|
@Singleton
|
||||||
public static class Server {
|
public static class Server {
|
||||||
private final GitRepositoryManager repoManager;
|
private final GitRepositoryManager repoManager;
|
||||||
private final AccountCache accountCache;
|
private final GitReferenceUpdated gitRefUpdated;
|
||||||
private final AllUsersName allUsersName;
|
private final AllUsersName allUsersName;
|
||||||
|
private final OutgoingEmailValidator emailValidator;
|
||||||
private final Provider<PersonIdent> serverIdent;
|
private final Provider<PersonIdent> serverIdent;
|
||||||
|
private final Provider<MetaDataUpdate.Server> metaDataUpdateServerFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public Server(
|
public Server(
|
||||||
GitRepositoryManager repoManager,
|
GitRepositoryManager repoManager,
|
||||||
AccountCache accountCache,
|
GitReferenceUpdated gitRefUpdated,
|
||||||
AllUsersName allUsersName,
|
AllUsersName allUsersName,
|
||||||
@GerritPersonIdent Provider<PersonIdent> serverIdent) {
|
OutgoingEmailValidator emailValidator,
|
||||||
|
@GerritPersonIdent Provider<PersonIdent> serverIdent,
|
||||||
|
Provider<MetaDataUpdate.Server> metaDataUpdateServerFactory) {
|
||||||
this.repoManager = repoManager;
|
this.repoManager = repoManager;
|
||||||
this.accountCache = accountCache;
|
this.gitRefUpdated = gitRefUpdated;
|
||||||
this.allUsersName = allUsersName;
|
this.allUsersName = allUsersName;
|
||||||
|
this.emailValidator = emailValidator;
|
||||||
this.serverIdent = serverIdent;
|
this.serverIdent = serverIdent;
|
||||||
|
this.metaDataUpdateServerFactory = metaDataUpdateServerFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountsUpdate create() {
|
public AccountsUpdate create() {
|
||||||
PersonIdent i = serverIdent.get();
|
PersonIdent i = serverIdent.get();
|
||||||
return new AccountsUpdate(repoManager, accountCache, allUsersName, i, i);
|
return new AccountsUpdate(
|
||||||
}
|
repoManager,
|
||||||
}
|
gitRefUpdated,
|
||||||
|
null,
|
||||||
/**
|
allUsersName,
|
||||||
* Factory to create an AccountsUpdate instance for updating accounts by the Gerrit server.
|
emailValidator,
|
||||||
*
|
i,
|
||||||
* <p>Using this class will not perform reindexing for the updated accounts and they will also not
|
() -> metaDataUpdateServerFactory.get().create(allUsersName));
|
||||||
* be evicted from the account cache.
|
|
||||||
*
|
|
||||||
* <p>The Gerrit server identity will be used as author and committer for all commits that update
|
|
||||||
* the accounts.
|
|
||||||
*/
|
|
||||||
@Singleton
|
|
||||||
public static class ServerNoReindex {
|
|
||||||
private final GitRepositoryManager repoManager;
|
|
||||||
private final AllUsersName allUsersName;
|
|
||||||
private final Provider<PersonIdent> serverIdent;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public ServerNoReindex(
|
|
||||||
GitRepositoryManager repoManager,
|
|
||||||
AllUsersName allUsersName,
|
|
||||||
@GerritPersonIdent Provider<PersonIdent> serverIdent) {
|
|
||||||
this.repoManager = repoManager;
|
|
||||||
this.allUsersName = allUsersName;
|
|
||||||
this.serverIdent = serverIdent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AccountsUpdate create() {
|
|
||||||
PersonIdent i = serverIdent.get();
|
|
||||||
return new AccountsUpdate(repoManager, null, allUsersName, i, i);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,29 +122,42 @@ public class AccountsUpdate {
|
|||||||
@Singleton
|
@Singleton
|
||||||
public static class User {
|
public static class User {
|
||||||
private final GitRepositoryManager repoManager;
|
private final GitRepositoryManager repoManager;
|
||||||
private final AccountCache accountCache;
|
private final GitReferenceUpdated gitRefUpdated;
|
||||||
private final AllUsersName allUsersName;
|
private final AllUsersName allUsersName;
|
||||||
|
private final OutgoingEmailValidator emailValidator;
|
||||||
private final Provider<PersonIdent> serverIdent;
|
private final Provider<PersonIdent> serverIdent;
|
||||||
private final Provider<IdentifiedUser> identifiedUser;
|
private final Provider<IdentifiedUser> identifiedUser;
|
||||||
|
private final Provider<MetaDataUpdate.User> metaDataUpdateUserFactory;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public User(
|
public User(
|
||||||
GitRepositoryManager repoManager,
|
GitRepositoryManager repoManager,
|
||||||
AccountCache accountCache,
|
GitReferenceUpdated gitRefUpdated,
|
||||||
AllUsersName allUsersName,
|
AllUsersName allUsersName,
|
||||||
|
OutgoingEmailValidator emailValidator,
|
||||||
@GerritPersonIdent Provider<PersonIdent> serverIdent,
|
@GerritPersonIdent Provider<PersonIdent> serverIdent,
|
||||||
Provider<IdentifiedUser> identifiedUser) {
|
Provider<IdentifiedUser> identifiedUser,
|
||||||
|
Provider<MetaDataUpdate.User> metaDataUpdateUserFactory) {
|
||||||
this.repoManager = repoManager;
|
this.repoManager = repoManager;
|
||||||
this.accountCache = accountCache;
|
this.gitRefUpdated = gitRefUpdated;
|
||||||
this.allUsersName = allUsersName;
|
this.allUsersName = allUsersName;
|
||||||
this.serverIdent = serverIdent;
|
this.serverIdent = serverIdent;
|
||||||
|
this.emailValidator = emailValidator;
|
||||||
this.identifiedUser = identifiedUser;
|
this.identifiedUser = identifiedUser;
|
||||||
|
this.metaDataUpdateUserFactory = metaDataUpdateUserFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccountsUpdate create() {
|
public AccountsUpdate create() {
|
||||||
|
IdentifiedUser user = identifiedUser.get();
|
||||||
PersonIdent i = serverIdent.get();
|
PersonIdent i = serverIdent.get();
|
||||||
return new AccountsUpdate(
|
return new AccountsUpdate(
|
||||||
repoManager, accountCache, allUsersName, createPersonIdent(i, identifiedUser.get()), i);
|
repoManager,
|
||||||
|
gitRefUpdated,
|
||||||
|
user,
|
||||||
|
allUsersName,
|
||||||
|
emailValidator,
|
||||||
|
createPersonIdent(i, user),
|
||||||
|
() -> metaDataUpdateUserFactory.get().create(allUsersName));
|
||||||
}
|
}
|
||||||
|
|
||||||
private PersonIdent createPersonIdent(PersonIdent ident, IdentifiedUser user) {
|
private PersonIdent createPersonIdent(PersonIdent ident, IdentifiedUser user) {
|
||||||
@ -153,117 +166,178 @@ public class AccountsUpdate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final GitRepositoryManager repoManager;
|
private final GitRepositoryManager repoManager;
|
||||||
@Nullable private final AccountCache accountCache;
|
private final GitReferenceUpdated gitRefUpdated;
|
||||||
|
@Nullable private final IdentifiedUser currentUser;
|
||||||
private final AllUsersName allUsersName;
|
private final AllUsersName allUsersName;
|
||||||
|
private final OutgoingEmailValidator emailValidator;
|
||||||
private final PersonIdent committerIdent;
|
private final PersonIdent committerIdent;
|
||||||
private final PersonIdent authorIdent;
|
private final MetaDataUpdateFactory metaDataUpdateFactory;
|
||||||
|
|
||||||
private AccountsUpdate(
|
private AccountsUpdate(
|
||||||
GitRepositoryManager repoManager,
|
GitRepositoryManager repoManager,
|
||||||
@Nullable AccountCache accountCache,
|
GitReferenceUpdated gitRefUpdated,
|
||||||
|
@Nullable IdentifiedUser currentUser,
|
||||||
AllUsersName allUsersName,
|
AllUsersName allUsersName,
|
||||||
|
OutgoingEmailValidator emailValidator,
|
||||||
PersonIdent committerIdent,
|
PersonIdent committerIdent,
|
||||||
PersonIdent authorIdent) {
|
MetaDataUpdateFactory metaDataUpdateFactory) {
|
||||||
this.repoManager = checkNotNull(repoManager, "repoManager");
|
this.repoManager = checkNotNull(repoManager, "repoManager");
|
||||||
this.accountCache = accountCache;
|
this.gitRefUpdated = checkNotNull(gitRefUpdated, "gitRefUpdated");
|
||||||
|
this.currentUser = currentUser;
|
||||||
this.allUsersName = checkNotNull(allUsersName, "allUsersName");
|
this.allUsersName = checkNotNull(allUsersName, "allUsersName");
|
||||||
|
this.emailValidator = checkNotNull(emailValidator, "emailValidator");
|
||||||
this.committerIdent = checkNotNull(committerIdent, "committerIdent");
|
this.committerIdent = checkNotNull(committerIdent, "committerIdent");
|
||||||
this.authorIdent = checkNotNull(authorIdent, "authorIdent");
|
this.metaDataUpdateFactory = checkNotNull(metaDataUpdateFactory, "metaDataUpdateFactory");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inserts a new account.
|
* Inserts a new account.
|
||||||
*
|
*
|
||||||
|
* @param db ReviewDb
|
||||||
|
* @param accountId ID of the new account
|
||||||
|
* @param init consumer to populate the new account
|
||||||
|
* @return the newly created account
|
||||||
|
* @throws OrmException if updating the database fails
|
||||||
* @throws OrmDuplicateKeyException if the account already exists
|
* @throws OrmDuplicateKeyException if the account already exists
|
||||||
* @throws IOException if updating the user branch fails
|
* @throws IOException if updating the user branch fails
|
||||||
|
* @throws ConfigInvalidException if any of the account fields has an invalid value
|
||||||
*/
|
*/
|
||||||
public void insert(ReviewDb db, Account account) throws OrmException, IOException {
|
public Account insert(ReviewDb db, Account.Id accountId, Consumer<Account> init)
|
||||||
|
throws OrmException, IOException, ConfigInvalidException {
|
||||||
|
AccountConfig accountConfig = read(accountId);
|
||||||
|
Account account = accountConfig.getNewAccount();
|
||||||
|
init.accept(account);
|
||||||
|
|
||||||
|
// Create in ReviewDb
|
||||||
db.accounts().insert(ImmutableSet.of(account));
|
db.accounts().insert(ImmutableSet.of(account));
|
||||||
createUserBranch(account);
|
|
||||||
evictAccount(account.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
// Create in NoteDb
|
||||||
* Inserts or updates an account.
|
commitNew(accountConfig);
|
||||||
*
|
return account;
|
||||||
* <p>If the account already exists, it is overwritten, otherwise it is inserted.
|
|
||||||
*/
|
|
||||||
public void upsert(ReviewDb db, Account account) throws OrmException, IOException {
|
|
||||||
db.accounts().upsert(ImmutableSet.of(account));
|
|
||||||
createUserBranchIfNeeded(account);
|
|
||||||
evictAccount(account.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Updates the account. */
|
|
||||||
public void update(ReviewDb db, Account account) throws OrmException, IOException {
|
|
||||||
db.accounts().update(ImmutableSet.of(account));
|
|
||||||
evictAccount(account.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the account and updates it atomically.
|
* Gets the account and updates it atomically.
|
||||||
*
|
*
|
||||||
|
* <p>Changing the registration date of an account is not supported.
|
||||||
|
*
|
||||||
* @param db ReviewDb
|
* @param db ReviewDb
|
||||||
* @param accountId ID of the account
|
* @param accountId ID of the account
|
||||||
* @param consumer consumer to update the account, only invoked if the account exists
|
* @param consumer consumer to update the account, only invoked if the account exists
|
||||||
* @return the updated account, {@code null} if the account doesn't exist
|
* @return the updated account, {@code null} if the account doesn't exist
|
||||||
* @throws OrmException if updating the account fails
|
* @throws OrmException if updating the database fails
|
||||||
|
* @throws IOException if updating the user branch fails
|
||||||
|
* @throws ConfigInvalidException if any of the account fields has an invalid value
|
||||||
*/
|
*/
|
||||||
public Account atomicUpdate(ReviewDb db, Account.Id accountId, Consumer<Account> consumer)
|
public Account update(ReviewDb db, Account.Id accountId, Consumer<Account> consumer)
|
||||||
throws OrmException, IOException {
|
throws OrmException, IOException, ConfigInvalidException {
|
||||||
Account account =
|
return update(db, accountId, ImmutableList.of(consumer));
|
||||||
db.accounts()
|
}
|
||||||
.atomicUpdate(
|
|
||||||
accountId,
|
/**
|
||||||
a -> {
|
* Gets the account and updates it atomically.
|
||||||
consumer.accept(a);
|
*
|
||||||
return a;
|
* <p>Changing the registration date of an account is not supported.
|
||||||
});
|
*
|
||||||
evictAccount(accountId);
|
* @param db ReviewDb
|
||||||
|
* @param accountId ID of the account
|
||||||
|
* @param consumers consumers to update the account, only invoked if the account exists
|
||||||
|
* @return the updated account, {@code null} if the account doesn't exist
|
||||||
|
* @throws OrmException if updating the database fails
|
||||||
|
* @throws IOException if updating the user branch fails
|
||||||
|
* @throws ConfigInvalidException if any of the account fields has an invalid value
|
||||||
|
*/
|
||||||
|
public Account update(ReviewDb db, Account.Id accountId, List<Consumer<Account>> consumers)
|
||||||
|
throws OrmException, IOException, ConfigInvalidException {
|
||||||
|
if (consumers.isEmpty()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update in ReviewDb
|
||||||
|
db.accounts()
|
||||||
|
.atomicUpdate(
|
||||||
|
accountId,
|
||||||
|
a -> {
|
||||||
|
consumers.stream().forEach(c -> c.accept(a));
|
||||||
|
return a;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update in NoteDb
|
||||||
|
AccountConfig accountConfig = read(accountId);
|
||||||
|
Account account = accountConfig.getAccount();
|
||||||
|
consumers.stream().forEach(c -> c.accept(account));
|
||||||
|
commit(accountConfig);
|
||||||
|
|
||||||
return account;
|
return account;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Deletes the account. */
|
/**
|
||||||
|
* Replaces the account.
|
||||||
|
*
|
||||||
|
* <p>The existing account with the same account ID is overwritten by the given account. Choosing
|
||||||
|
* to overwrite an account means that any updates that were done to the account by a racing
|
||||||
|
* request after the account was read are lost. Updates are also lost if the account was read from
|
||||||
|
* a stale account index. This is why using {@link #update(ReviewDb,
|
||||||
|
* com.google.gerrit.reviewdb.client.Account.Id, Consumer)} to do an atomic update is always
|
||||||
|
* preferred.
|
||||||
|
*
|
||||||
|
* <p>Changing the registration date of an account is not supported.
|
||||||
|
*
|
||||||
|
* @param db ReviewDb
|
||||||
|
* @param account the new account
|
||||||
|
* @throws OrmException if updating the database fails
|
||||||
|
* @throws IOException if updating the user branch fails
|
||||||
|
* @throws ConfigInvalidException if any of the account fields has an invalid value
|
||||||
|
* @see #update(ReviewDb, com.google.gerrit.reviewdb.client.Account.Id, Consumer)
|
||||||
|
*/
|
||||||
|
public void replace(ReviewDb db, Account account)
|
||||||
|
throws OrmException, IOException, ConfigInvalidException {
|
||||||
|
// Update in ReviewDb
|
||||||
|
db.accounts().update(ImmutableSet.of(account));
|
||||||
|
|
||||||
|
// Update in NoteDb
|
||||||
|
AccountConfig accountConfig = read(account.getId());
|
||||||
|
accountConfig.setAccount(account);
|
||||||
|
commit(accountConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the account.
|
||||||
|
*
|
||||||
|
* @param db ReviewDb
|
||||||
|
* @param account the account that should be deleted
|
||||||
|
* @throws OrmException if updating the database fails
|
||||||
|
* @throws IOException if updating the user branch fails
|
||||||
|
*/
|
||||||
public void delete(ReviewDb db, Account account) throws OrmException, IOException {
|
public void delete(ReviewDb db, Account account) throws OrmException, IOException {
|
||||||
|
// Delete in ReviewDb
|
||||||
db.accounts().delete(ImmutableSet.of(account));
|
db.accounts().delete(ImmutableSet.of(account));
|
||||||
|
|
||||||
|
// Delete in NoteDb
|
||||||
deleteUserBranch(account.getId());
|
deleteUserBranch(account.getId());
|
||||||
evictAccount(account.getId());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Deletes the account. */
|
/**
|
||||||
|
* Deletes the account.
|
||||||
|
*
|
||||||
|
* @param db ReviewDb
|
||||||
|
* @param accountId the ID of the account that should be deleted
|
||||||
|
* @throws OrmException if updating the database fails
|
||||||
|
* @throws IOException if updating the user branch fails
|
||||||
|
*/
|
||||||
public void deleteByKey(ReviewDb db, Account.Id accountId) throws OrmException, IOException {
|
public void deleteByKey(ReviewDb db, Account.Id accountId) throws OrmException, IOException {
|
||||||
|
// Delete in ReviewDb
|
||||||
db.accounts().deleteKeys(ImmutableSet.of(accountId));
|
db.accounts().deleteKeys(ImmutableSet.of(accountId));
|
||||||
|
|
||||||
|
// Delete in NoteDb
|
||||||
deleteUserBranch(accountId);
|
deleteUserBranch(accountId);
|
||||||
evictAccount(accountId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createUserBranch(Account account) throws IOException {
|
|
||||||
try (Repository repo = repoManager.openRepository(allUsersName);
|
|
||||||
ObjectInserter oi = repo.newObjectInserter()) {
|
|
||||||
String refName = RefNames.refsUsers(account.getId());
|
|
||||||
if (repo.exactRef(refName) != null) {
|
|
||||||
throw new IOException(
|
|
||||||
String.format(
|
|
||||||
"User branch %s for newly created account %s already exists.",
|
|
||||||
refName, account.getId().get()));
|
|
||||||
}
|
|
||||||
createUserBranch(
|
|
||||||
repo, oi, committerIdent, authorIdent, account.getId(), account.getRegisteredOn());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void createUserBranchIfNeeded(Account account) throws IOException {
|
|
||||||
try (Repository repo = repoManager.openRepository(allUsersName);
|
|
||||||
ObjectInserter oi = repo.newObjectInserter()) {
|
|
||||||
if (repo.exactRef(RefNames.refsUsers(account.getId())) == null) {
|
|
||||||
createUserBranch(
|
|
||||||
repo, oi, committerIdent, authorIdent, account.getId(), account.getRegisteredOn());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void createUserBranch(
|
public static void createUserBranch(
|
||||||
Repository repo,
|
Repository repo,
|
||||||
|
Project.NameKey project,
|
||||||
|
GitReferenceUpdated gitRefUpdated,
|
||||||
|
@Nullable IdentifiedUser user,
|
||||||
ObjectInserter oi,
|
ObjectInserter oi,
|
||||||
PersonIdent committerIdent,
|
PersonIdent committerIdent,
|
||||||
PersonIdent authorIdent,
|
PersonIdent authorIdent,
|
||||||
@ -283,6 +357,7 @@ public class AccountsUpdate {
|
|||||||
if (result != Result.NEW) {
|
if (result != Result.NEW) {
|
||||||
throw new IOException(String.format("Failed to update ref %s: %s", refName, result.name()));
|
throw new IOException(String.format("Failed to update ref %s: %s", refName, result.name()));
|
||||||
}
|
}
|
||||||
|
gitRefUpdated.fire(project, ru, user != null ? user.getAccount() : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ObjectId createInitialEmptyCommit(
|
private static ObjectId createInitialEmptyCommit(
|
||||||
@ -307,12 +382,18 @@ public class AccountsUpdate {
|
|||||||
|
|
||||||
private void deleteUserBranch(Account.Id accountId) throws IOException {
|
private void deleteUserBranch(Account.Id accountId) throws IOException {
|
||||||
try (Repository repo = repoManager.openRepository(allUsersName)) {
|
try (Repository repo = repoManager.openRepository(allUsersName)) {
|
||||||
deleteUserBranch(repo, committerIdent, accountId);
|
deleteUserBranch(repo, allUsersName, gitRefUpdated, currentUser, committerIdent, accountId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void deleteUserBranch(
|
public static void deleteUserBranch(
|
||||||
Repository repo, PersonIdent refLogIdent, Account.Id accountId) throws IOException {
|
Repository repo,
|
||||||
|
Project.NameKey project,
|
||||||
|
GitReferenceUpdated gitRefUpdated,
|
||||||
|
@Nullable IdentifiedUser user,
|
||||||
|
PersonIdent refLogIdent,
|
||||||
|
Account.Id accountId)
|
||||||
|
throws IOException {
|
||||||
String refName = RefNames.refsUsers(accountId);
|
String refName = RefNames.refsUsers(accountId);
|
||||||
Ref ref = repo.exactRef(refName);
|
Ref ref = repo.exactRef(refName);
|
||||||
if (ref == null) {
|
if (ref == null) {
|
||||||
@ -329,11 +410,37 @@ public class AccountsUpdate {
|
|||||||
if (result != Result.FORCED) {
|
if (result != Result.FORCED) {
|
||||||
throw new IOException(String.format("Failed to delete ref %s: %s", refName, result.name()));
|
throw new IOException(String.format("Failed to delete ref %s: %s", refName, result.name()));
|
||||||
}
|
}
|
||||||
|
gitRefUpdated.fire(project, ru, user != null ? user.getAccount() : null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void evictAccount(Account.Id accountId) throws IOException {
|
private AccountConfig read(Account.Id accountId) throws IOException, ConfigInvalidException {
|
||||||
if (accountCache != null) {
|
try (Repository repo = repoManager.openRepository(allUsersName)) {
|
||||||
accountCache.evict(accountId);
|
AccountConfig accountConfig = new AccountConfig(emailValidator, accountId);
|
||||||
|
accountConfig.load(repo);
|
||||||
|
return accountConfig;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void commitNew(AccountConfig accountConfig) throws IOException {
|
||||||
|
// When creating a new account we must allow empty commits so that the user branch gets created
|
||||||
|
// with an empty commit when no account properties are set and hence no 'account.config' file
|
||||||
|
// will be created.
|
||||||
|
commit(accountConfig, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void commit(AccountConfig accountConfig) throws IOException {
|
||||||
|
commit(accountConfig, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void commit(AccountConfig accountConfig, boolean allowEmptyCommit) throws IOException {
|
||||||
|
try (MetaDataUpdate md = metaDataUpdateFactory.create()) {
|
||||||
|
md.setAllowEmpty(allowEmptyCommit);
|
||||||
|
accountConfig.commit(md);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
private static interface MetaDataUpdateFactory {
|
||||||
|
MetaDataUpdate create() throws IOException;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ package com.google.gerrit.server.account;
|
|||||||
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO;
|
import static com.google.gerrit.server.account.externalids.ExternalId.SCHEME_MAILTO;
|
||||||
|
|
||||||
import com.google.gerrit.audit.AuditService;
|
import com.google.gerrit.audit.AuditService;
|
||||||
import com.google.gerrit.common.TimeUtil;
|
import com.google.gerrit.common.Nullable;
|
||||||
import com.google.gerrit.common.data.GlobalCapability;
|
import com.google.gerrit.common.data.GlobalCapability;
|
||||||
import com.google.gerrit.common.data.GroupDescriptions;
|
import com.google.gerrit.common.data.GroupDescriptions;
|
||||||
import com.google.gerrit.common.errors.InvalidSshKeyException;
|
import com.google.gerrit.common.errors.InvalidSshKeyException;
|
||||||
@ -113,12 +113,15 @@ public class CreateAccount implements RestModifyView<TopLevelResource, AccountIn
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response<AccountInfo> apply(TopLevelResource rsrc, AccountInput input)
|
public Response<AccountInfo> apply(TopLevelResource rsrc, @Nullable AccountInput input)
|
||||||
|
throws BadRequestException, ResourceConflictException, UnprocessableEntityException,
|
||||||
|
OrmException, IOException, ConfigInvalidException {
|
||||||
|
return apply(input != null ? input : new AccountInput());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Response<AccountInfo> apply(AccountInput input)
|
||||||
throws BadRequestException, ResourceConflictException, UnprocessableEntityException,
|
throws BadRequestException, ResourceConflictException, UnprocessableEntityException,
|
||||||
OrmException, IOException, ConfigInvalidException {
|
OrmException, IOException, ConfigInvalidException {
|
||||||
if (input == null) {
|
|
||||||
input = new AccountInput();
|
|
||||||
}
|
|
||||||
if (input.username != null && !username.equals(input.username)) {
|
if (input.username != null && !username.equals(input.username)) {
|
||||||
throw new BadRequestException("username must match URL");
|
throw new BadRequestException("username must match URL");
|
||||||
}
|
}
|
||||||
@ -171,10 +174,15 @@ public class CreateAccount implements RestModifyView<TopLevelResource, AccountIn
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Account a = new Account(id, TimeUtil.nowTs());
|
accountsUpdate
|
||||||
a.setFullName(input.name);
|
.create()
|
||||||
a.setPreferredEmail(input.email);
|
.insert(
|
||||||
accountsUpdate.create().insert(db, a);
|
db,
|
||||||
|
id,
|
||||||
|
a -> {
|
||||||
|
a.setFullName(input.name);
|
||||||
|
a.setPreferredEmail(input.email);
|
||||||
|
});
|
||||||
|
|
||||||
for (AccountGroup.Id groupId : groups) {
|
for (AccountGroup.Id groupId : groups) {
|
||||||
AccountGroupMember m = new AccountGroupMember(new AccountGroupMember.Key(id, groupId));
|
AccountGroupMember m = new AccountGroupMember(new AccountGroupMember.Key(id, groupId));
|
||||||
|
@ -31,6 +31,7 @@ import com.google.inject.Provider;
|
|||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@RequiresCapability(GlobalCapability.MODIFY_ACCOUNT)
|
@RequiresCapability(GlobalCapability.MODIFY_ACCOUNT)
|
||||||
@Singleton
|
@Singleton
|
||||||
@ -53,7 +54,7 @@ public class DeleteActive implements RestModifyView<AccountResource, Input> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response<?> apply(AccountResource rsrc, Input input)
|
public Response<?> apply(AccountResource rsrc, Input input)
|
||||||
throws RestApiException, OrmException, IOException {
|
throws RestApiException, OrmException, IOException, ConfigInvalidException {
|
||||||
if (self.get() == rsrc.getUser()) {
|
if (self.get() == rsrc.getUser()) {
|
||||||
throw new ResourceConflictException("cannot deactivate own account");
|
throw new ResourceConflictException("cannot deactivate own account");
|
||||||
}
|
}
|
||||||
@ -62,7 +63,7 @@ public class DeleteActive implements RestModifyView<AccountResource, Input> {
|
|||||||
Account account =
|
Account account =
|
||||||
accountsUpdate
|
accountsUpdate
|
||||||
.create()
|
.create()
|
||||||
.atomicUpdate(
|
.update(
|
||||||
dbProvider.get(),
|
dbProvider.get(),
|
||||||
rsrc.getUser().getAccountId(),
|
rsrc.getUser().getAccountId(),
|
||||||
a -> {
|
a -> {
|
||||||
|
@ -28,6 +28,7 @@ import com.google.inject.Provider;
|
|||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@RequiresCapability(GlobalCapability.MODIFY_ACCOUNT)
|
@RequiresCapability(GlobalCapability.MODIFY_ACCOUNT)
|
||||||
@Singleton
|
@Singleton
|
||||||
@ -45,12 +46,12 @@ public class PutActive implements RestModifyView<AccountResource, Input> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response<String> apply(AccountResource rsrc, Input input)
|
public Response<String> apply(AccountResource rsrc, Input input)
|
||||||
throws ResourceNotFoundException, OrmException, IOException {
|
throws ResourceNotFoundException, OrmException, IOException, ConfigInvalidException {
|
||||||
AtomicBoolean alreadyActive = new AtomicBoolean(false);
|
AtomicBoolean alreadyActive = new AtomicBoolean(false);
|
||||||
Account account =
|
Account account =
|
||||||
accountsUpdate
|
accountsUpdate
|
||||||
.create()
|
.create()
|
||||||
.atomicUpdate(
|
.update(
|
||||||
dbProvider.get(),
|
dbProvider.get(),
|
||||||
rsrc.getUser().getAccountId(),
|
rsrc.getUser().getAccountId(),
|
||||||
a -> {
|
a -> {
|
||||||
|
@ -35,6 +35,7 @@ import com.google.inject.Inject;
|
|||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class PutName implements RestModifyView<AccountResource, Input> {
|
public class PutName implements RestModifyView<AccountResource, Input> {
|
||||||
@ -65,7 +66,7 @@ public class PutName implements RestModifyView<AccountResource, Input> {
|
|||||||
@Override
|
@Override
|
||||||
public Response<String> apply(AccountResource rsrc, Input input)
|
public Response<String> apply(AccountResource rsrc, Input input)
|
||||||
throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException,
|
throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException,
|
||||||
IOException, PermissionBackendException {
|
IOException, PermissionBackendException, ConfigInvalidException {
|
||||||
if (self.get() != rsrc.getUser()) {
|
if (self.get() != rsrc.getUser()) {
|
||||||
permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
|
permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
|
||||||
}
|
}
|
||||||
@ -73,7 +74,8 @@ public class PutName implements RestModifyView<AccountResource, Input> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Response<String> apply(IdentifiedUser user, Input input)
|
public Response<String> apply(IdentifiedUser user, Input input)
|
||||||
throws MethodNotAllowedException, ResourceNotFoundException, OrmException, IOException {
|
throws MethodNotAllowedException, ResourceNotFoundException, OrmException, IOException,
|
||||||
|
ConfigInvalidException {
|
||||||
if (input == null) {
|
if (input == null) {
|
||||||
input = new Input();
|
input = new Input();
|
||||||
}
|
}
|
||||||
@ -86,7 +88,7 @@ public class PutName implements RestModifyView<AccountResource, Input> {
|
|||||||
Account account =
|
Account account =
|
||||||
accountsUpdate
|
accountsUpdate
|
||||||
.create()
|
.create()
|
||||||
.atomicUpdate(dbProvider.get(), user.getAccountId(), a -> a.setFullName(newName));
|
.update(dbProvider.get(), user.getAccountId(), a -> a.setFullName(newName));
|
||||||
if (account == null) {
|
if (account == null) {
|
||||||
throw new ResourceNotFoundException("account not found");
|
throw new ResourceNotFoundException("account not found");
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ import com.google.inject.Provider;
|
|||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class PutPreferred implements RestModifyView<AccountResource.Email, Input> {
|
public class PutPreferred implements RestModifyView<AccountResource.Email, Input> {
|
||||||
@ -57,7 +58,7 @@ public class PutPreferred implements RestModifyView<AccountResource.Email, Input
|
|||||||
@Override
|
@Override
|
||||||
public Response<String> apply(AccountResource.Email rsrc, Input input)
|
public Response<String> apply(AccountResource.Email rsrc, Input input)
|
||||||
throws AuthException, ResourceNotFoundException, OrmException, IOException,
|
throws AuthException, ResourceNotFoundException, OrmException, IOException,
|
||||||
PermissionBackendException {
|
PermissionBackendException, ConfigInvalidException {
|
||||||
if (self.get() != rsrc.getUser()) {
|
if (self.get() != rsrc.getUser()) {
|
||||||
permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
|
permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
|
||||||
}
|
}
|
||||||
@ -65,12 +66,12 @@ public class PutPreferred implements RestModifyView<AccountResource.Email, Input
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Response<String> apply(IdentifiedUser user, String email)
|
public Response<String> apply(IdentifiedUser user, String email)
|
||||||
throws ResourceNotFoundException, OrmException, IOException {
|
throws ResourceNotFoundException, OrmException, IOException, ConfigInvalidException {
|
||||||
AtomicBoolean alreadyPreferred = new AtomicBoolean(false);
|
AtomicBoolean alreadyPreferred = new AtomicBoolean(false);
|
||||||
Account account =
|
Account account =
|
||||||
accountsUpdate
|
accountsUpdate
|
||||||
.create()
|
.create()
|
||||||
.atomicUpdate(
|
.update(
|
||||||
dbProvider.get(),
|
dbProvider.get(),
|
||||||
user.getAccountId(),
|
user.getAccountId(),
|
||||||
a -> {
|
a -> {
|
||||||
|
@ -33,6 +33,7 @@ import com.google.inject.Inject;
|
|||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class PutStatus implements RestModifyView<AccountResource, Input> {
|
public class PutStatus implements RestModifyView<AccountResource, Input> {
|
||||||
@ -66,7 +67,7 @@ public class PutStatus implements RestModifyView<AccountResource, Input> {
|
|||||||
@Override
|
@Override
|
||||||
public Response<String> apply(AccountResource rsrc, Input input)
|
public Response<String> apply(AccountResource rsrc, Input input)
|
||||||
throws AuthException, ResourceNotFoundException, OrmException, IOException,
|
throws AuthException, ResourceNotFoundException, OrmException, IOException,
|
||||||
PermissionBackendException {
|
PermissionBackendException, ConfigInvalidException {
|
||||||
if (self.get() != rsrc.getUser()) {
|
if (self.get() != rsrc.getUser()) {
|
||||||
permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
|
permissionBackend.user(self).check(GlobalPermission.MODIFY_ACCOUNT);
|
||||||
}
|
}
|
||||||
@ -74,7 +75,7 @@ public class PutStatus implements RestModifyView<AccountResource, Input> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Response<String> apply(IdentifiedUser user, Input input)
|
public Response<String> apply(IdentifiedUser user, Input input)
|
||||||
throws ResourceNotFoundException, OrmException, IOException {
|
throws ResourceNotFoundException, OrmException, IOException, ConfigInvalidException {
|
||||||
if (input == null) {
|
if (input == null) {
|
||||||
input = new Input();
|
input = new Input();
|
||||||
}
|
}
|
||||||
@ -83,7 +84,7 @@ public class PutStatus implements RestModifyView<AccountResource, Input> {
|
|||||||
Account account =
|
Account account =
|
||||||
accountsUpdate
|
accountsUpdate
|
||||||
.create()
|
.create()
|
||||||
.atomicUpdate(
|
.update(
|
||||||
dbProvider.get(),
|
dbProvider.get(),
|
||||||
user.getAccountId(),
|
user.getAccountId(),
|
||||||
a -> a.setStatus(Strings.nullToEmpty(newStatus)));
|
a -> a.setStatus(Strings.nullToEmpty(newStatus)));
|
||||||
|
@ -27,6 +27,7 @@ import com.google.inject.Inject;
|
|||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.assistedinject.Assisted;
|
import com.google.inject.assistedinject.Assisted;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.kohsuke.args4j.CmdLineException;
|
import org.kohsuke.args4j.CmdLineException;
|
||||||
import org.kohsuke.args4j.CmdLineParser;
|
import org.kohsuke.args4j.CmdLineParser;
|
||||||
import org.kohsuke.args4j.OptionDef;
|
import org.kohsuke.args4j.OptionDef;
|
||||||
@ -82,8 +83,12 @@ public class AccountIdHandler extends OptionHandler<Account.Id> {
|
|||||||
throw new CmdLineException(owner, "user \"" + token + "\" not found");
|
throw new CmdLineException(owner, "user \"" + token + "\" not found");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (OrmException | IOException e) {
|
} catch (OrmException e) {
|
||||||
throw new CmdLineException(owner, "database is down");
|
throw new CmdLineException(owner, "database is down");
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new CmdLineException(owner, "Failed to load account", e);
|
||||||
|
} catch (ConfigInvalidException e) {
|
||||||
|
throw new CmdLineException(owner, "Invalid account config", e);
|
||||||
}
|
}
|
||||||
setter.addValue(accountId);
|
setter.addValue(accountId);
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -41,7 +41,9 @@ import com.google.gwtorm.server.OrmException;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class Abandon extends RetryingRestModifyView<ChangeResource, AbandonInput, ChangeInfo>
|
public class Abandon extends RetryingRestModifyView<ChangeResource, AbandonInput, ChangeInfo>
|
||||||
@ -68,7 +70,8 @@ public class Abandon extends RetryingRestModifyView<ChangeResource, AbandonInput
|
|||||||
@Override
|
@Override
|
||||||
protected ChangeInfo applyImpl(
|
protected ChangeInfo applyImpl(
|
||||||
BatchUpdate.Factory updateFactory, ChangeResource req, AbandonInput input)
|
BatchUpdate.Factory updateFactory, ChangeResource req, AbandonInput input)
|
||||||
throws RestApiException, UpdateException, OrmException, PermissionBackendException {
|
throws RestApiException, UpdateException, OrmException, PermissionBackendException,
|
||||||
|
IOException, ConfigInvalidException {
|
||||||
req.permissions().database(dbProvider).check(ChangePermission.ABANDON);
|
req.permissions().database(dbProvider).check(ChangePermission.ABANDON);
|
||||||
|
|
||||||
NotifyHandling notify = input.notify == null ? defaultNotify(req.getControl()) : input.notify;
|
NotifyHandling notify = input.notify == null ? defaultNotify(req.getControl()) : input.notify;
|
||||||
|
@ -39,6 +39,7 @@ import com.google.inject.Inject;
|
|||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class CherryPick
|
public class CherryPick
|
||||||
@ -67,7 +68,7 @@ public class CherryPick
|
|||||||
public ChangeInfo applyImpl(
|
public ChangeInfo applyImpl(
|
||||||
BatchUpdate.Factory updateFactory, RevisionResource rsrc, CherryPickInput input)
|
BatchUpdate.Factory updateFactory, RevisionResource rsrc, CherryPickInput input)
|
||||||
throws OrmException, IOException, UpdateException, RestApiException,
|
throws OrmException, IOException, UpdateException, RestApiException,
|
||||||
PermissionBackendException {
|
PermissionBackendException, ConfigInvalidException {
|
||||||
input.parent = input.parent == null ? 1 : input.parent;
|
input.parent = input.parent == null ? 1 : input.parent;
|
||||||
if (input.message == null || input.message.trim().isEmpty()) {
|
if (input.message == null || input.message.trim().isEmpty()) {
|
||||||
throw new BadRequestException("message must be non-empty");
|
throw new BadRequestException("message must be non-empty");
|
||||||
|
@ -62,6 +62,7 @@ import java.io.IOException;
|
|||||||
import java.sql.Timestamp;
|
import java.sql.Timestamp;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.errors.InvalidObjectIdException;
|
import org.eclipse.jgit.errors.InvalidObjectIdException;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.ObjectInserter;
|
import org.eclipse.jgit.lib.ObjectInserter;
|
||||||
@ -124,7 +125,7 @@ public class CherryPickChange {
|
|||||||
CherryPickInput input,
|
CherryPickInput input,
|
||||||
RefControl refControl)
|
RefControl refControl)
|
||||||
throws OrmException, IOException, InvalidChangeOperationException, IntegrationException,
|
throws OrmException, IOException, InvalidChangeOperationException, IntegrationException,
|
||||||
UpdateException, RestApiException {
|
UpdateException, RestApiException, ConfigInvalidException {
|
||||||
return cherryPick(
|
return cherryPick(
|
||||||
batchUpdateFactory,
|
batchUpdateFactory,
|
||||||
change.getId(),
|
change.getId(),
|
||||||
@ -148,7 +149,7 @@ public class CherryPickChange {
|
|||||||
CherryPickInput input,
|
CherryPickInput input,
|
||||||
RefControl destRefControl)
|
RefControl destRefControl)
|
||||||
throws OrmException, IOException, InvalidChangeOperationException, IntegrationException,
|
throws OrmException, IOException, InvalidChangeOperationException, IntegrationException,
|
||||||
UpdateException, RestApiException {
|
UpdateException, RestApiException, ConfigInvalidException {
|
||||||
|
|
||||||
IdentifiedUser identifiedUser = user.get();
|
IdentifiedUser identifiedUser = user.get();
|
||||||
try (Repository git = gitManager.openRepository(project);
|
try (Repository git = gitManager.openRepository(project);
|
||||||
@ -320,7 +321,7 @@ public class CherryPickChange {
|
|||||||
ChangeControl destCtl,
|
ChangeControl destCtl,
|
||||||
CodeReviewCommit cherryPickCommit,
|
CodeReviewCommit cherryPickCommit,
|
||||||
CherryPickInput input)
|
CherryPickInput input)
|
||||||
throws IOException, OrmException, BadRequestException {
|
throws IOException, OrmException, BadRequestException, ConfigInvalidException {
|
||||||
Change destChange = destCtl.getChange();
|
Change destChange = destCtl.getChange();
|
||||||
PatchSet.Id psId = ChangeUtil.nextPatchSetId(git, destChange.currentPatchSetId());
|
PatchSet.Id psId = ChangeUtil.nextPatchSetId(git, destChange.currentPatchSetId());
|
||||||
PatchSet current = psUtil.current(dbProvider.get(), destCtl.getNotes());
|
PatchSet current = psUtil.current(dbProvider.get(), destCtl.getNotes());
|
||||||
@ -343,7 +344,7 @@ public class CherryPickChange {
|
|||||||
Branch.NameKey sourceBranch,
|
Branch.NameKey sourceBranch,
|
||||||
ObjectId sourceCommit,
|
ObjectId sourceCommit,
|
||||||
CherryPickInput input)
|
CherryPickInput input)
|
||||||
throws OrmException, IOException, BadRequestException {
|
throws OrmException, IOException, BadRequestException, ConfigInvalidException {
|
||||||
Change.Id changeId = new Change.Id(seq.nextChangeId());
|
Change.Id changeId = new Change.Id(seq.nextChangeId());
|
||||||
ChangeInserter ins =
|
ChangeInserter ins =
|
||||||
changeInserterFactory.create(changeId, cherryPickCommit, refName).setTopic(topic);
|
changeInserterFactory.create(changeId, cherryPickCommit, refName).setTopic(topic);
|
||||||
|
@ -39,6 +39,7 @@ import com.google.inject.Inject;
|
|||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@ -67,7 +68,7 @@ public class CherryPickCommit
|
|||||||
public ChangeInfo applyImpl(
|
public ChangeInfo applyImpl(
|
||||||
BatchUpdate.Factory updateFactory, CommitResource rsrc, CherryPickInput input)
|
BatchUpdate.Factory updateFactory, CommitResource rsrc, CherryPickInput input)
|
||||||
throws OrmException, IOException, UpdateException, RestApiException,
|
throws OrmException, IOException, UpdateException, RestApiException,
|
||||||
PermissionBackendException {
|
PermissionBackendException, ConfigInvalidException {
|
||||||
RevCommit commit = rsrc.getCommit();
|
RevCommit commit = rsrc.getCommit();
|
||||||
String message = Strings.nullToEmpty(input.message).trim();
|
String message = Strings.nullToEmpty(input.message).trim();
|
||||||
input.message = message.isEmpty() ? commit.getFullMessage() : message;
|
input.message = message.isEmpty() ? commit.getFullMessage() : message;
|
||||||
|
@ -68,6 +68,7 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||||
import org.eclipse.jgit.errors.MissingObjectException;
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||||
@ -226,7 +227,7 @@ public class ConsistencyChecker {
|
|||||||
if (accounts.get(db.get(), change().getOwner()) == null) {
|
if (accounts.get(db.get(), change().getOwner()) == null) {
|
||||||
problem("Missing change owner: " + change().getOwner());
|
problem("Missing change owner: " + change().getOwner());
|
||||||
}
|
}
|
||||||
} catch (OrmException e) {
|
} catch (OrmException | IOException | ConfigInvalidException e) {
|
||||||
error("Failed to look up owner", e);
|
error("Failed to look up owner", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,6 +74,7 @@ import java.sql.Timestamp;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.lib.CommitBuilder;
|
import org.eclipse.jgit.lib.CommitBuilder;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
@ -151,7 +152,7 @@ public class CreateChange
|
|||||||
protected Response<ChangeInfo> applyImpl(
|
protected Response<ChangeInfo> applyImpl(
|
||||||
BatchUpdate.Factory updateFactory, TopLevelResource parent, ChangeInput input)
|
BatchUpdate.Factory updateFactory, TopLevelResource parent, ChangeInput input)
|
||||||
throws OrmException, IOException, InvalidChangeOperationException, RestApiException,
|
throws OrmException, IOException, InvalidChangeOperationException, RestApiException,
|
||||||
UpdateException, PermissionBackendException {
|
UpdateException, PermissionBackendException, ConfigInvalidException {
|
||||||
if (Strings.isNullOrEmpty(input.project)) {
|
if (Strings.isNullOrEmpty(input.project)) {
|
||||||
throw new BadRequestException("project must be non-empty");
|
throw new BadRequestException("project must be non-empty");
|
||||||
}
|
}
|
||||||
|
@ -31,10 +31,12 @@ import com.google.gwtorm.server.OrmException;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class NotifyUtil {
|
public class NotifyUtil {
|
||||||
@ -76,7 +78,7 @@ public class NotifyUtil {
|
|||||||
|
|
||||||
public ListMultimap<RecipientType, Account.Id> resolveAccounts(
|
public ListMultimap<RecipientType, Account.Id> resolveAccounts(
|
||||||
@Nullable Map<RecipientType, NotifyInfo> notifyDetails)
|
@Nullable Map<RecipientType, NotifyInfo> notifyDetails)
|
||||||
throws OrmException, BadRequestException {
|
throws OrmException, BadRequestException, IOException, ConfigInvalidException {
|
||||||
if (isNullOrEmpty(notifyDetails)) {
|
if (isNullOrEmpty(notifyDetails)) {
|
||||||
return ImmutableListMultimap.of();
|
return ImmutableListMultimap.of();
|
||||||
}
|
}
|
||||||
@ -96,7 +98,7 @@ public class NotifyUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<Account.Id> find(ReviewDb db, List<String> nameOrEmails)
|
private List<Account.Id> find(ReviewDb db, List<String> nameOrEmails)
|
||||||
throws OrmException, BadRequestException {
|
throws OrmException, BadRequestException, IOException, ConfigInvalidException {
|
||||||
List<String> missing = new ArrayList<>(nameOrEmails.size());
|
List<String> missing = new ArrayList<>(nameOrEmails.size());
|
||||||
List<Account.Id> r = new ArrayList<>(nameOrEmails.size());
|
List<Account.Id> r = new ArrayList<>(nameOrEmails.size());
|
||||||
for (String nameOrEmail : nameOrEmails) {
|
for (String nameOrEmail : nameOrEmails) {
|
||||||
|
@ -126,6 +126,7 @@ import java.util.Map;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.OptionalInt;
|
import java.util.OptionalInt;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@ -190,14 +191,14 @@ public class PostReview
|
|||||||
protected Response<ReviewResult> applyImpl(
|
protected Response<ReviewResult> applyImpl(
|
||||||
BatchUpdate.Factory updateFactory, RevisionResource revision, ReviewInput input)
|
BatchUpdate.Factory updateFactory, RevisionResource revision, ReviewInput input)
|
||||||
throws RestApiException, UpdateException, OrmException, IOException,
|
throws RestApiException, UpdateException, OrmException, IOException,
|
||||||
PermissionBackendException {
|
PermissionBackendException, ConfigInvalidException {
|
||||||
return apply(updateFactory, revision, input, TimeUtil.nowTs());
|
return apply(updateFactory, revision, input, TimeUtil.nowTs());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response<ReviewResult> apply(
|
public Response<ReviewResult> apply(
|
||||||
BatchUpdate.Factory updateFactory, RevisionResource revision, ReviewInput input, Timestamp ts)
|
BatchUpdate.Factory updateFactory, RevisionResource revision, ReviewInput input, Timestamp ts)
|
||||||
throws RestApiException, UpdateException, OrmException, IOException,
|
throws RestApiException, UpdateException, OrmException, IOException,
|
||||||
PermissionBackendException {
|
PermissionBackendException, ConfigInvalidException {
|
||||||
// Respect timestamp, but truncate at change created-on time.
|
// Respect timestamp, but truncate at change created-on time.
|
||||||
ts = Ordering.natural().max(ts, revision.getChange().getCreatedOn());
|
ts = Ordering.natural().max(ts, revision.getChange().getCreatedOn());
|
||||||
if (revision.getEdit().isPresent()) {
|
if (revision.getEdit().isPresent()) {
|
||||||
@ -367,7 +368,7 @@ public class PostReview
|
|||||||
|
|
||||||
private RevisionResource onBehalfOf(RevisionResource rev, ReviewInput in)
|
private RevisionResource onBehalfOf(RevisionResource rev, ReviewInput in)
|
||||||
throws BadRequestException, AuthException, UnprocessableEntityException, OrmException,
|
throws BadRequestException, AuthException, UnprocessableEntityException, OrmException,
|
||||||
PermissionBackendException {
|
PermissionBackendException, IOException, ConfigInvalidException {
|
||||||
if (in.labels == null || in.labels.isEmpty()) {
|
if (in.labels == null || in.labels.isEmpty()) {
|
||||||
throw new AuthException(
|
throw new AuthException(
|
||||||
String.format("label required to post review on behalf of \"%s\"", in.onBehalfOf));
|
String.format("label required to post review on behalf of \"%s\"", in.onBehalfOf));
|
||||||
|
@ -76,6 +76,7 @@ import java.text.MessageFormat;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@ -148,7 +149,7 @@ public class PostReviewers
|
|||||||
protected AddReviewerResult applyImpl(
|
protected AddReviewerResult applyImpl(
|
||||||
BatchUpdate.Factory updateFactory, ChangeResource rsrc, AddReviewerInput input)
|
BatchUpdate.Factory updateFactory, ChangeResource rsrc, AddReviewerInput input)
|
||||||
throws IOException, OrmException, RestApiException, UpdateException,
|
throws IOException, OrmException, RestApiException, UpdateException,
|
||||||
PermissionBackendException {
|
PermissionBackendException, ConfigInvalidException {
|
||||||
if (input.reviewer == null) {
|
if (input.reviewer == null) {
|
||||||
throw new BadRequestException("missing reviewer field");
|
throw new BadRequestException("missing reviewer field");
|
||||||
}
|
}
|
||||||
@ -170,7 +171,7 @@ public class PostReviewers
|
|||||||
|
|
||||||
public Addition prepareApplication(
|
public Addition prepareApplication(
|
||||||
ChangeResource rsrc, AddReviewerInput input, boolean allowGroup)
|
ChangeResource rsrc, AddReviewerInput input, boolean allowGroup)
|
||||||
throws OrmException, IOException, PermissionBackendException {
|
throws OrmException, IOException, PermissionBackendException, ConfigInvalidException {
|
||||||
String reviewer = input.reviewer;
|
String reviewer = input.reviewer;
|
||||||
ReviewerState state = input.state();
|
ReviewerState state = input.state();
|
||||||
NotifyHandling notify = input.notify;
|
NotifyHandling notify = input.notify;
|
||||||
@ -219,7 +220,7 @@ public class PostReviewers
|
|||||||
ListMultimap<RecipientType, Account.Id> accountsToNotify,
|
ListMultimap<RecipientType, Account.Id> accountsToNotify,
|
||||||
boolean allowGroup,
|
boolean allowGroup,
|
||||||
boolean allowByEmail)
|
boolean allowByEmail)
|
||||||
throws OrmException, PermissionBackendException {
|
throws OrmException, PermissionBackendException, IOException, ConfigInvalidException {
|
||||||
Account.Id accountId = null;
|
Account.Id accountId = null;
|
||||||
try {
|
try {
|
||||||
accountId = accounts.parse(reviewer).getAccountId();
|
accountId = accounts.parse(reviewer).getAccountId();
|
||||||
|
@ -45,6 +45,7 @@ import java.io.IOException;
|
|||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import org.apache.commons.compress.archivers.ArchiveOutputStream;
|
import org.apache.commons.compress.archivers.ArchiveOutputStream;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.eclipse.jgit.lib.NullProgressMonitor;
|
import org.eclipse.jgit.lib.NullProgressMonitor;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
@ -82,7 +83,7 @@ public class PreviewSubmit implements RestReadView<RevisionResource> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BinaryResult apply(RevisionResource rsrc)
|
public BinaryResult apply(RevisionResource rsrc)
|
||||||
throws OrmException, RestApiException, UpdateException {
|
throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException {
|
||||||
if (Strings.isNullOrEmpty(format)) {
|
if (Strings.isNullOrEmpty(format)) {
|
||||||
throw new BadRequestException("format is not specified");
|
throw new BadRequestException("format is not specified");
|
||||||
}
|
}
|
||||||
@ -110,7 +111,7 @@ public class PreviewSubmit implements RestReadView<RevisionResource> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private BinaryResult getBundles(RevisionResource rsrc, ArchiveFormat f)
|
private BinaryResult getBundles(RevisionResource rsrc, ArchiveFormat f)
|
||||||
throws OrmException, RestApiException, UpdateException {
|
throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException {
|
||||||
ReviewDb db = dbProvider.get();
|
ReviewDb db = dbProvider.get();
|
||||||
ChangeControl control = rsrc.getControl();
|
ChangeControl control = rsrc.getControl();
|
||||||
IdentifiedUser caller = control.getUser().asIdentifiedUser();
|
IdentifiedUser caller = control.getUser().asIdentifiedUser();
|
||||||
@ -125,7 +126,12 @@ public class PreviewSubmit implements RestReadView<RevisionResource> {
|
|||||||
.setContentType(f.getMimeType())
|
.setContentType(f.getMimeType())
|
||||||
.setAttachmentName("submit-preview-" + change.getChangeId() + "." + format);
|
.setAttachmentName("submit-preview-" + change.getChangeId() + "." + format);
|
||||||
return bin;
|
return bin;
|
||||||
} catch (OrmException | RestApiException | UpdateException | RuntimeException e) {
|
} catch (OrmException
|
||||||
|
| RestApiException
|
||||||
|
| UpdateException
|
||||||
|
| IOException
|
||||||
|
| ConfigInvalidException
|
||||||
|
| RuntimeException e) {
|
||||||
op.close();
|
op.close();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,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.Optional;
|
import java.util.Optional;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class PublishChangeEdit
|
public class PublishChangeEdit
|
||||||
@ -85,7 +86,8 @@ public class PublishChangeEdit
|
|||||||
@Override
|
@Override
|
||||||
protected Response<?> applyImpl(
|
protected Response<?> applyImpl(
|
||||||
BatchUpdate.Factory updateFactory, ChangeResource rsrc, PublishChangeEditInput in)
|
BatchUpdate.Factory updateFactory, ChangeResource rsrc, PublishChangeEditInput in)
|
||||||
throws IOException, OrmException, RestApiException, UpdateException {
|
throws IOException, OrmException, RestApiException, UpdateException,
|
||||||
|
ConfigInvalidException {
|
||||||
CreateChange.checkValidCLA(rsrc.getControl().getProjectControl());
|
CreateChange.checkValidCLA(rsrc.getControl().getProjectControl());
|
||||||
Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getChange());
|
Optional<ChangeEdit> edit = editUtil.byChange(rsrc.getChange());
|
||||||
if (!edit.isPresent()) {
|
if (!edit.isPresent()) {
|
||||||
|
@ -42,6 +42,7 @@ import com.google.inject.Inject;
|
|||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class PutAssignee extends RetryingRestModifyView<ChangeResource, AssigneeInput, AccountInfo>
|
public class PutAssignee extends RetryingRestModifyView<ChangeResource, AssigneeInput, AccountInfo>
|
||||||
@ -73,7 +74,7 @@ public class PutAssignee extends RetryingRestModifyView<ChangeResource, Assignee
|
|||||||
protected AccountInfo applyImpl(
|
protected AccountInfo applyImpl(
|
||||||
BatchUpdate.Factory updateFactory, ChangeResource rsrc, AssigneeInput input)
|
BatchUpdate.Factory updateFactory, ChangeResource rsrc, AssigneeInput input)
|
||||||
throws RestApiException, UpdateException, OrmException, IOException,
|
throws RestApiException, UpdateException, OrmException, IOException,
|
||||||
PermissionBackendException {
|
PermissionBackendException, ConfigInvalidException {
|
||||||
rsrc.permissions().check(ChangePermission.EDIT_ASSIGNEE);
|
rsrc.permissions().check(ChangePermission.EDIT_ASSIGNEE);
|
||||||
|
|
||||||
input.assignee = Strings.nullToEmpty(input.assignee).trim();
|
input.assignee = Strings.nullToEmpty(input.assignee).trim();
|
||||||
@ -109,7 +110,7 @@ public class PutAssignee extends RetryingRestModifyView<ChangeResource, Assignee
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Addition addAssigneeAsCC(ChangeResource rsrc, String assignee)
|
private Addition addAssigneeAsCC(ChangeResource rsrc, String assignee)
|
||||||
throws OrmException, IOException, PermissionBackendException {
|
throws OrmException, IOException, PermissionBackendException, ConfigInvalidException {
|
||||||
AddReviewerInput reviewerInput = new AddReviewerInput();
|
AddReviewerInput reviewerInput = new AddReviewerInput();
|
||||||
reviewerInput.reviewer = assignee;
|
reviewerInput.reviewer = assignee;
|
||||||
reviewerInput.state = ReviewerState.CC;
|
reviewerInput.state = ReviewerState.CC;
|
||||||
|
@ -51,6 +51,7 @@ import java.util.TimeZone;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.lib.CommitBuilder;
|
import org.eclipse.jgit.lib.CommitBuilder;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
@ -107,7 +108,7 @@ public class PutMessage
|
|||||||
protected Response<String> applyImpl(
|
protected Response<String> applyImpl(
|
||||||
BatchUpdate.Factory updateFactory, ChangeResource resource, Input input)
|
BatchUpdate.Factory updateFactory, ChangeResource resource, Input input)
|
||||||
throws IOException, UnchangedCommitMessageException, RestApiException, UpdateException,
|
throws IOException, UnchangedCommitMessageException, RestApiException, UpdateException,
|
||||||
PermissionBackendException, OrmException {
|
PermissionBackendException, OrmException, ConfigInvalidException {
|
||||||
PatchSet ps = psUtil.current(db.get(), resource.getNotes());
|
PatchSet ps = psUtil.current(db.get(), resource.getNotes());
|
||||||
if (ps == null) {
|
if (ps == null) {
|
||||||
throw new ResourceConflictException("current revision is missing");
|
throw new ResourceConflictException("current revision is missing");
|
||||||
|
@ -30,7 +30,9 @@ import com.google.gwtorm.server.OrmException;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class Reviewers implements ChildCollection<ChangeResource, ReviewerResource> {
|
public class Reviewers implements ChildCollection<ChangeResource, ReviewerResource> {
|
||||||
@ -69,7 +71,8 @@ public class Reviewers implements ChildCollection<ChangeResource, ReviewerResour
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReviewerResource parse(ChangeResource rsrc, IdString id)
|
public ReviewerResource parse(ChangeResource rsrc, IdString id)
|
||||||
throws OrmException, ResourceNotFoundException, AuthException {
|
throws OrmException, ResourceNotFoundException, AuthException, IOException,
|
||||||
|
ConfigInvalidException {
|
||||||
Address address = Address.tryParse(id.get());
|
Address address = Address.tryParse(id.get());
|
||||||
|
|
||||||
Account.Id accountId = null;
|
Account.Id accountId = null;
|
||||||
|
@ -31,7 +31,9 @@ import com.google.gwtorm.server.OrmException;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class RevisionReviewers implements ChildCollection<RevisionResource, ReviewerResource> {
|
public class RevisionReviewers implements ChildCollection<RevisionResource, ReviewerResource> {
|
||||||
@ -70,7 +72,8 @@ public class RevisionReviewers implements ChildCollection<RevisionResource, Revi
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReviewerResource parse(RevisionResource rsrc, IdString id)
|
public ReviewerResource parse(RevisionResource rsrc, IdString id)
|
||||||
throws OrmException, ResourceNotFoundException, AuthException, MethodNotAllowedException {
|
throws OrmException, ResourceNotFoundException, AuthException, MethodNotAllowedException,
|
||||||
|
IOException, ConfigInvalidException {
|
||||||
if (!rsrc.isCurrent()) {
|
if (!rsrc.isCurrent()) {
|
||||||
throw new MethodNotAllowedException("Cannot access on non-current patch set");
|
throw new MethodNotAllowedException("Cannot access on non-current patch set");
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,7 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
@ -202,7 +203,7 @@ public class Submit
|
|||||||
@Override
|
@Override
|
||||||
public Output apply(RevisionResource rsrc, SubmitInput input)
|
public Output apply(RevisionResource rsrc, SubmitInput input)
|
||||||
throws RestApiException, RepositoryNotFoundException, IOException, OrmException,
|
throws RestApiException, RepositoryNotFoundException, IOException, OrmException,
|
||||||
PermissionBackendException, UpdateException {
|
PermissionBackendException, UpdateException, ConfigInvalidException {
|
||||||
input.onBehalfOf = Strings.emptyToNull(input.onBehalfOf);
|
input.onBehalfOf = Strings.emptyToNull(input.onBehalfOf);
|
||||||
IdentifiedUser submitter;
|
IdentifiedUser submitter;
|
||||||
if (input.onBehalfOf != null) {
|
if (input.onBehalfOf != null) {
|
||||||
@ -216,7 +217,7 @@ public class Submit
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Change mergeChange(RevisionResource rsrc, IdentifiedUser submitter, SubmitInput input)
|
public Change mergeChange(RevisionResource rsrc, IdentifiedUser submitter, SubmitInput input)
|
||||||
throws OrmException, RestApiException, IOException, UpdateException {
|
throws OrmException, RestApiException, IOException, UpdateException, ConfigInvalidException {
|
||||||
Change change = rsrc.getChange();
|
Change change = rsrc.getChange();
|
||||||
if (!change.getStatus().isOpen()) {
|
if (!change.getStatus().isOpen()) {
|
||||||
throw new ResourceConflictException("change is " + ChangeUtil.status(change));
|
throw new ResourceConflictException("change is " + ChangeUtil.status(change));
|
||||||
@ -249,7 +250,7 @@ public class Submit
|
|||||||
if (msg != null) {
|
if (msg != null) {
|
||||||
throw new ResourceConflictException(msg.getMessage());
|
throw new ResourceConflictException(msg.getMessage());
|
||||||
}
|
}
|
||||||
//$FALL-THROUGH$
|
// $FALL-THROUGH$
|
||||||
case ABANDONED:
|
case ABANDONED:
|
||||||
case DRAFT:
|
case DRAFT:
|
||||||
default:
|
default:
|
||||||
@ -478,7 +479,8 @@ public class Submit
|
|||||||
}
|
}
|
||||||
|
|
||||||
private IdentifiedUser onBehalfOf(RevisionResource rsrc, SubmitInput in)
|
private IdentifiedUser onBehalfOf(RevisionResource rsrc, SubmitInput in)
|
||||||
throws AuthException, UnprocessableEntityException, OrmException, PermissionBackendException {
|
throws AuthException, UnprocessableEntityException, OrmException, PermissionBackendException,
|
||||||
|
IOException, ConfigInvalidException {
|
||||||
PermissionBackend.ForChange perm = rsrc.permissions().database(dbProvider);
|
PermissionBackend.ForChange perm = rsrc.permissions().database(dbProvider);
|
||||||
perm.check(ChangePermission.SUBMIT);
|
perm.check(ChangePermission.SUBMIT);
|
||||||
perm.check(ChangePermission.SUBMIT_AS);
|
perm.check(ChangePermission.SUBMIT_AS);
|
||||||
@ -527,7 +529,7 @@ public class Submit
|
|||||||
@Override
|
@Override
|
||||||
public ChangeInfo apply(ChangeResource rsrc, SubmitInput input)
|
public ChangeInfo apply(ChangeResource rsrc, SubmitInput input)
|
||||||
throws RestApiException, RepositoryNotFoundException, IOException, OrmException,
|
throws RestApiException, RepositoryNotFoundException, IOException, OrmException,
|
||||||
PermissionBackendException, UpdateException {
|
PermissionBackendException, UpdateException, ConfigInvalidException {
|
||||||
PatchSet ps = psUtil.current(dbProvider.get(), rsrc.getNotes());
|
PatchSet ps = psUtil.current(dbProvider.get(), rsrc.getNotes());
|
||||||
if (ps == null) {
|
if (ps == null) {
|
||||||
throw new ResourceConflictException("current revision is missing");
|
throw new ResourceConflictException("current revision is missing");
|
||||||
|
@ -34,6 +34,7 @@ import com.google.inject.Inject;
|
|||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.kohsuke.args4j.Option;
|
import org.kohsuke.args4j.Option;
|
||||||
|
|
||||||
@ -66,7 +67,7 @@ public class SuggestChangeReviewers extends SuggestReviewers
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SuggestedReviewerInfo> apply(ChangeResource rsrc)
|
public List<SuggestedReviewerInfo> apply(ChangeResource rsrc)
|
||||||
throws AuthException, BadRequestException, OrmException, IOException {
|
throws AuthException, BadRequestException, OrmException, IOException, ConfigInvalidException {
|
||||||
if (!self.get().isIdentifiedUser()) {
|
if (!self.get().isIdentifiedUser()) {
|
||||||
throw new AuthException("Authentication required");
|
throw new AuthException("Authentication required");
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ import com.google.inject.Provider;
|
|||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class CheckAccess implements RestModifyView<ConfigResource, AccessCheckInput> {
|
public class CheckAccess implements RestModifyView<ConfigResource, AccessCheckInput> {
|
||||||
@ -67,7 +68,8 @@ public class CheckAccess implements RestModifyView<ConfigResource, AccessCheckIn
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AccessCheckInfo apply(ConfigResource unused, AccessCheckInput input)
|
public AccessCheckInfo apply(ConfigResource unused, AccessCheckInput input)
|
||||||
throws OrmException, PermissionBackendException, RestApiException, IOException {
|
throws OrmException, PermissionBackendException, RestApiException, IOException,
|
||||||
|
ConfigInvalidException {
|
||||||
permissionBackend.user(currentUser.get()).check(GlobalPermission.ADMINISTRATE_SERVER);
|
permissionBackend.user(currentUser.get()).check(GlobalPermission.ADMINISTRATE_SERVER);
|
||||||
|
|
||||||
if (input == null) {
|
if (input == null) {
|
||||||
|
@ -126,6 +126,7 @@ import com.google.gerrit.server.git.strategy.SubmitStrategy;
|
|||||||
import com.google.gerrit.server.git.validators.CommitValidationListener;
|
import com.google.gerrit.server.git.validators.CommitValidationListener;
|
||||||
import com.google.gerrit.server.git.validators.MergeValidationListener;
|
import com.google.gerrit.server.git.validators.MergeValidationListener;
|
||||||
import com.google.gerrit.server.git.validators.MergeValidators;
|
import com.google.gerrit.server.git.validators.MergeValidators;
|
||||||
|
import com.google.gerrit.server.git.validators.MergeValidators.AccountValidator;
|
||||||
import com.google.gerrit.server.git.validators.MergeValidators.ProjectConfigValidator;
|
import com.google.gerrit.server.git.validators.MergeValidators.ProjectConfigValidator;
|
||||||
import com.google.gerrit.server.git.validators.OnSubmitValidationListener;
|
import com.google.gerrit.server.git.validators.OnSubmitValidationListener;
|
||||||
import com.google.gerrit.server.git.validators.OnSubmitValidators;
|
import com.google.gerrit.server.git.validators.OnSubmitValidators;
|
||||||
@ -389,6 +390,7 @@ public class GerritGlobalModule extends FactoryModule {
|
|||||||
bind(AnonymousUser.class);
|
bind(AnonymousUser.class);
|
||||||
|
|
||||||
factory(AbandonOp.Factory.class);
|
factory(AbandonOp.Factory.class);
|
||||||
|
factory(AccountValidator.Factory.class);
|
||||||
factory(RefOperationValidators.Factory.class);
|
factory(RefOperationValidators.Factory.class);
|
||||||
factory(OnSubmitValidators.Factory.class);
|
factory(OnSubmitValidators.Factory.class);
|
||||||
factory(MergeValidators.Factory.class);
|
factory(MergeValidators.Factory.class);
|
||||||
|
@ -88,6 +88,7 @@ import java.util.LinkedHashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
@ -430,7 +431,7 @@ public class MergeOp implements AutoCloseable {
|
|||||||
boolean checkSubmitRules,
|
boolean checkSubmitRules,
|
||||||
SubmitInput submitInput,
|
SubmitInput submitInput,
|
||||||
boolean dryrun)
|
boolean dryrun)
|
||||||
throws OrmException, RestApiException, UpdateException {
|
throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException {
|
||||||
this.submitInput = submitInput;
|
this.submitInput = submitInput;
|
||||||
this.accountsToNotify = notifyUtil.resolveAccounts(submitInput.notifyDetails);
|
this.accountsToNotify = notifyUtil.resolveAccounts(submitInput.notifyDetails);
|
||||||
this.dryrun = dryrun;
|
this.dryrun = dryrun;
|
||||||
|
@ -84,7 +84,6 @@ import com.google.gerrit.server.IdentifiedUser;
|
|||||||
import com.google.gerrit.server.PatchSetUtil;
|
import com.google.gerrit.server.PatchSetUtil;
|
||||||
import com.google.gerrit.server.Sequences;
|
import com.google.gerrit.server.Sequences;
|
||||||
import com.google.gerrit.server.account.AccountResolver;
|
import com.google.gerrit.server.account.AccountResolver;
|
||||||
import com.google.gerrit.server.account.Accounts;
|
|
||||||
import com.google.gerrit.server.account.AccountsUpdate;
|
import com.google.gerrit.server.account.AccountsUpdate;
|
||||||
import com.google.gerrit.server.change.ChangeInserter;
|
import com.google.gerrit.server.change.ChangeInserter;
|
||||||
import com.google.gerrit.server.change.SetHashtagsOp;
|
import com.google.gerrit.server.change.SetHashtagsOp;
|
||||||
@ -155,6 +154,7 @@ import java.util.concurrent.ExecutionException;
|
|||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||||
import org.eclipse.jgit.errors.MissingObjectException;
|
import org.eclipse.jgit.errors.MissingObjectException;
|
||||||
import org.eclipse.jgit.lib.Constants;
|
import org.eclipse.jgit.lib.Constants;
|
||||||
@ -301,7 +301,6 @@ public class ReceiveCommits {
|
|||||||
private final Sequences seq;
|
private final Sequences seq;
|
||||||
private final Provider<InternalChangeQuery> queryProvider;
|
private final Provider<InternalChangeQuery> queryProvider;
|
||||||
private final ChangeNotes.Factory notesFactory;
|
private final ChangeNotes.Factory notesFactory;
|
||||||
private final Accounts accounts;
|
|
||||||
private final AccountsUpdate.Server accountsUpdate;
|
private final AccountsUpdate.Server accountsUpdate;
|
||||||
private final AccountResolver accountResolver;
|
private final AccountResolver accountResolver;
|
||||||
private final PermissionBackend permissionBackend;
|
private final PermissionBackend permissionBackend;
|
||||||
@ -376,7 +375,6 @@ public class ReceiveCommits {
|
|||||||
Sequences seq,
|
Sequences seq,
|
||||||
Provider<InternalChangeQuery> queryProvider,
|
Provider<InternalChangeQuery> queryProvider,
|
||||||
ChangeNotes.Factory notesFactory,
|
ChangeNotes.Factory notesFactory,
|
||||||
Accounts accounts,
|
|
||||||
AccountsUpdate.Server accountsUpdate,
|
AccountsUpdate.Server accountsUpdate,
|
||||||
AccountResolver accountResolver,
|
AccountResolver accountResolver,
|
||||||
PermissionBackend permissionBackend,
|
PermissionBackend permissionBackend,
|
||||||
@ -416,7 +414,6 @@ public class ReceiveCommits {
|
|||||||
this.seq = seq;
|
this.seq = seq;
|
||||||
this.queryProvider = queryProvider;
|
this.queryProvider = queryProvider;
|
||||||
this.notesFactory = notesFactory;
|
this.notesFactory = notesFactory;
|
||||||
this.accounts = accounts;
|
|
||||||
this.accountsUpdate = accountsUpdate;
|
this.accountsUpdate = accountsUpdate;
|
||||||
this.accountResolver = accountResolver;
|
this.accountResolver = accountResolver;
|
||||||
this.permissionBackend = permissionBackend;
|
this.permissionBackend = permissionBackend;
|
||||||
@ -815,7 +812,11 @@ public class ReceiveCommits {
|
|||||||
} catch (ResourceConflictException e) {
|
} catch (ResourceConflictException e) {
|
||||||
addMessage(e.getMessage());
|
addMessage(e.getMessage());
|
||||||
reject(magicBranchCmd, "conflict");
|
reject(magicBranchCmd, "conflict");
|
||||||
} catch (RestApiException | OrmException | UpdateException e) {
|
} catch (RestApiException
|
||||||
|
| OrmException
|
||||||
|
| UpdateException
|
||||||
|
| IOException
|
||||||
|
| ConfigInvalidException e) {
|
||||||
logError("Error submitting changes to " + project.getName(), e);
|
logError("Error submitting changes to " + project.getName(), e);
|
||||||
reject(magicBranchCmd, "error during submit");
|
reject(magicBranchCmd, "error during submit");
|
||||||
}
|
}
|
||||||
@ -2258,7 +2259,7 @@ public class ReceiveCommits {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void submit(Collection<CreateRequest> create, Collection<ReplaceRequest> replace)
|
private void submit(Collection<CreateRequest> create, Collection<ReplaceRequest> replace)
|
||||||
throws OrmException, RestApiException, UpdateException {
|
throws OrmException, RestApiException, UpdateException, IOException, ConfigInvalidException {
|
||||||
Map<ObjectId, Change> bySha = Maps.newHashMapWithExpectedSize(create.size() + replace.size());
|
Map<ObjectId, Change> bySha = Maps.newHashMapWithExpectedSize(create.size() + replace.size());
|
||||||
for (CreateRequest r : create) {
|
for (CreateRequest r : create) {
|
||||||
checkNotNull(r.change, "cannot submit new change %s; op may not have run", r.changeId);
|
checkNotNull(r.change, "cannot submit new change %s; op may not have run", r.changeId);
|
||||||
@ -2783,13 +2784,22 @@ public class ReceiveCommits {
|
|||||||
|
|
||||||
if (defaultName && user.hasEmailAddress(c.getCommitterIdent().getEmailAddress())) {
|
if (defaultName && user.hasEmailAddress(c.getCommitterIdent().getEmailAddress())) {
|
||||||
try {
|
try {
|
||||||
Account a = accounts.get(db, user.getAccountId());
|
String committerName = c.getCommitterIdent().getName();
|
||||||
if (a != null && Strings.isNullOrEmpty(a.getFullName())) {
|
Account account =
|
||||||
a.setFullName(c.getCommitterIdent().getName());
|
accountsUpdate
|
||||||
accountsUpdate.create().update(db, a);
|
.create()
|
||||||
user.getAccount().setFullName(a.getFullName());
|
.update(
|
||||||
|
db,
|
||||||
|
user.getAccountId(),
|
||||||
|
a -> {
|
||||||
|
if (Strings.isNullOrEmpty(a.getFullName())) {
|
||||||
|
a.setFullName(committerName);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (account != null && Strings.isNullOrEmpty(account.getFullName())) {
|
||||||
|
user.getAccount().setFullName(account.getFullName());
|
||||||
}
|
}
|
||||||
} catch (OrmException e) {
|
} catch (OrmException | IOException | ConfigInvalidException e) {
|
||||||
logWarn("Cannot default full_name", e);
|
logWarn("Cannot default full_name", e);
|
||||||
} finally {
|
} finally {
|
||||||
defaultName = false;
|
defaultName = false;
|
||||||
|
@ -75,6 +75,7 @@ public abstract class VersionedMetaData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected RevCommit revision;
|
protected RevCommit revision;
|
||||||
|
protected RevWalk rw;
|
||||||
protected ObjectReader reader;
|
protected ObjectReader reader;
|
||||||
protected ObjectInserter inserter;
|
protected ObjectInserter inserter;
|
||||||
protected DirCache newTree;
|
protected DirCache newTree;
|
||||||
@ -153,11 +154,13 @@ public abstract class VersionedMetaData {
|
|||||||
* @throws ConfigInvalidException
|
* @throws ConfigInvalidException
|
||||||
*/
|
*/
|
||||||
public void load(RevWalk walk, ObjectId id) throws IOException, ConfigInvalidException {
|
public void load(RevWalk walk, ObjectId id) throws IOException, ConfigInvalidException {
|
||||||
|
this.rw = walk;
|
||||||
this.reader = walk.getObjectReader();
|
this.reader = walk.getObjectReader();
|
||||||
try {
|
try {
|
||||||
revision = id != null ? walk.parseCommit(id) : null;
|
revision = id != null ? walk.parseCommit(id) : null;
|
||||||
onLoad();
|
onLoad();
|
||||||
} finally {
|
} finally {
|
||||||
|
walk = null;
|
||||||
reader = null;
|
reader = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import com.google.gerrit.reviewdb.client.Account;
|
|||||||
import com.google.gerrit.reviewdb.client.RefNames;
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
import com.google.gerrit.server.GerritPersonIdent;
|
import com.google.gerrit.server.GerritPersonIdent;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
|
import com.google.gerrit.server.account.AccountConfig;
|
||||||
import com.google.gerrit.server.account.WatchConfig;
|
import com.google.gerrit.server.account.WatchConfig;
|
||||||
import com.google.gerrit.server.account.externalids.ExternalIdsConsistencyChecker;
|
import com.google.gerrit.server.account.externalids.ExternalIdsConsistencyChecker;
|
||||||
import com.google.gerrit.server.config.AllUsersName;
|
import com.google.gerrit.server.config.AllUsersName;
|
||||||
@ -57,9 +58,11 @@ import java.net.URL;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.lib.PersonIdent;
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.eclipse.jgit.notes.NoteMap;
|
import org.eclipse.jgit.notes.NoteMap;
|
||||||
@ -67,6 +70,7 @@ import org.eclipse.jgit.revwalk.FooterKey;
|
|||||||
import org.eclipse.jgit.revwalk.FooterLine;
|
import org.eclipse.jgit.revwalk.FooterLine;
|
||||||
import org.eclipse.jgit.revwalk.RevCommit;
|
import org.eclipse.jgit.revwalk.RevCommit;
|
||||||
import org.eclipse.jgit.revwalk.RevWalk;
|
import org.eclipse.jgit.revwalk.RevWalk;
|
||||||
|
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||||
import org.eclipse.jgit.util.SystemReader;
|
import org.eclipse.jgit.util.SystemReader;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -120,7 +124,8 @@ public class CommitValidators {
|
|||||||
new ConfigValidator(refctl, rw, allUsers),
|
new ConfigValidator(refctl, rw, allUsers),
|
||||||
new BannedCommitsValidator(rejectCommits),
|
new BannedCommitsValidator(rejectCommits),
|
||||||
new PluginCommitValidationListener(pluginValidators),
|
new PluginCommitValidationListener(pluginValidators),
|
||||||
new ExternalIdUpdateListener(allUsers, externalIdsConsistencyChecker)));
|
new ExternalIdUpdateListener(allUsers, externalIdsConsistencyChecker),
|
||||||
|
new AccountValidator(allUsers)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommitValidators forGerritCommits(
|
public CommitValidators forGerritCommits(
|
||||||
@ -135,7 +140,8 @@ public class CommitValidators {
|
|||||||
new ChangeIdValidator(refctl, canonicalWebUrl, installCommitMsgHookCommand, sshInfo),
|
new ChangeIdValidator(refctl, canonicalWebUrl, installCommitMsgHookCommand, sshInfo),
|
||||||
new ConfigValidator(refctl, rw, allUsers),
|
new ConfigValidator(refctl, rw, allUsers),
|
||||||
new PluginCommitValidationListener(pluginValidators),
|
new PluginCommitValidationListener(pluginValidators),
|
||||||
new ExternalIdUpdateListener(allUsers, externalIdsConsistencyChecker)));
|
new ExternalIdUpdateListener(allUsers, externalIdsConsistencyChecker),
|
||||||
|
new AccountValidator(allUsers)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public CommitValidators forMergedCommits(PermissionBackend.ForRef perm, RefControl refControl) {
|
public CommitValidators forMergedCommits(PermissionBackend.ForRef perm, RefControl refControl) {
|
||||||
@ -679,6 +685,60 @@ public class CommitValidators {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Rejects updates to 'account.config' in user branches. */
|
||||||
|
public static class AccountValidator implements CommitValidationListener {
|
||||||
|
private final AllUsersName allUsers;
|
||||||
|
|
||||||
|
public AccountValidator(AllUsersName allUsers) {
|
||||||
|
this.allUsers = allUsers;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<CommitValidationMessage> onCommitReceived(CommitReceivedEvent receiveEvent)
|
||||||
|
throws CommitValidationException {
|
||||||
|
if (!allUsers.equals(receiveEvent.project.getNameKey())) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (receiveEvent.command.getRefName().startsWith(MagicBranch.NEW_CHANGE)) {
|
||||||
|
// no validation on push for review, will be checked on submit by
|
||||||
|
// MergeValidators.AccountValidator
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
Account.Id accountId = Account.Id.fromRef(receiveEvent.refName);
|
||||||
|
if (accountId == null) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ObjectId newBlobId = getAccountConfigBlobId(receiveEvent.revWalk, receiveEvent.commit);
|
||||||
|
|
||||||
|
ObjectId oldId = receiveEvent.command.getOldId();
|
||||||
|
ObjectId oldBlobId =
|
||||||
|
!ObjectId.zeroId().equals(oldId)
|
||||||
|
? getAccountConfigBlobId(receiveEvent.revWalk, oldId)
|
||||||
|
: null;
|
||||||
|
if (!Objects.equals(oldBlobId, newBlobId)) {
|
||||||
|
throw new CommitValidationException("account update not allowed");
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
String m = String.format("Validating update for account %s failed", accountId.get());
|
||||||
|
log.error(m, e);
|
||||||
|
throw new CommitValidationException(m, e);
|
||||||
|
}
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private ObjectId getAccountConfigBlobId(RevWalk rw, ObjectId id) throws IOException {
|
||||||
|
RevCommit commit = rw.parseCommit(id);
|
||||||
|
try (TreeWalk tw =
|
||||||
|
TreeWalk.forPath(rw.getObjectReader(), AccountConfig.ACCOUNT_CONFIG, commit.getTree())) {
|
||||||
|
return tw != null ? tw.getObjectId(0) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static CommitValidationMessage invalidEmail(
|
private static CommitValidationMessage invalidEmail(
|
||||||
RevCommit c,
|
RevCommit c,
|
||||||
String type,
|
String type,
|
||||||
|
@ -20,12 +20,16 @@ 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.extensions.registration.DynamicSet;
|
import com.google.gerrit.extensions.registration.DynamicSet;
|
||||||
import com.google.gerrit.extensions.restapi.AuthException;
|
import com.google.gerrit.extensions.restapi.AuthException;
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
import com.google.gerrit.reviewdb.client.Branch;
|
import com.google.gerrit.reviewdb.client.Branch;
|
||||||
import com.google.gerrit.reviewdb.client.PatchSet;
|
import com.google.gerrit.reviewdb.client.PatchSet;
|
||||||
import com.google.gerrit.reviewdb.client.Project;
|
import com.google.gerrit.reviewdb.client.Project;
|
||||||
import com.google.gerrit.reviewdb.client.RefNames;
|
import com.google.gerrit.reviewdb.client.RefNames;
|
||||||
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
|
import com.google.gerrit.server.account.AccountConfig;
|
||||||
import com.google.gerrit.server.config.AllProjectsName;
|
import com.google.gerrit.server.config.AllProjectsName;
|
||||||
|
import com.google.gerrit.server.config.AllUsersName;
|
||||||
import com.google.gerrit.server.config.PluginConfig;
|
import com.google.gerrit.server.config.PluginConfig;
|
||||||
import com.google.gerrit.server.config.ProjectConfigEntry;
|
import com.google.gerrit.server.config.ProjectConfigEntry;
|
||||||
import com.google.gerrit.server.git.CodeReviewCommit;
|
import com.google.gerrit.server.git.CodeReviewCommit;
|
||||||
@ -35,7 +39,10 @@ import com.google.gerrit.server.permissions.PermissionBackend;
|
|||||||
import com.google.gerrit.server.permissions.PermissionBackendException;
|
import com.google.gerrit.server.permissions.PermissionBackendException;
|
||||||
import com.google.gerrit.server.project.ProjectCache;
|
import com.google.gerrit.server.project.ProjectCache;
|
||||||
import com.google.gerrit.server.project.ProjectState;
|
import com.google.gerrit.server.project.ProjectState;
|
||||||
|
import com.google.gerrit.server.query.change.ChangeData;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Provider;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.eclipse.jgit.errors.ConfigInvalidException;
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
@ -48,6 +55,7 @@ public class MergeValidators {
|
|||||||
|
|
||||||
private final DynamicSet<MergeValidationListener> mergeValidationListeners;
|
private final DynamicSet<MergeValidationListener> mergeValidationListeners;
|
||||||
private final ProjectConfigValidator.Factory projectConfigValidatorFactory;
|
private final ProjectConfigValidator.Factory projectConfigValidatorFactory;
|
||||||
|
private final AccountValidator.Factory accountValidatorFactory;
|
||||||
|
|
||||||
public interface Factory {
|
public interface Factory {
|
||||||
MergeValidators create();
|
MergeValidators create();
|
||||||
@ -56,9 +64,11 @@ public class MergeValidators {
|
|||||||
@Inject
|
@Inject
|
||||||
MergeValidators(
|
MergeValidators(
|
||||||
DynamicSet<MergeValidationListener> mergeValidationListeners,
|
DynamicSet<MergeValidationListener> mergeValidationListeners,
|
||||||
ProjectConfigValidator.Factory projectConfigValidatorFactory) {
|
ProjectConfigValidator.Factory projectConfigValidatorFactory,
|
||||||
|
AccountValidator.Factory accountValidatorFactory) {
|
||||||
this.mergeValidationListeners = mergeValidationListeners;
|
this.mergeValidationListeners = mergeValidationListeners;
|
||||||
this.projectConfigValidatorFactory = projectConfigValidatorFactory;
|
this.projectConfigValidatorFactory = projectConfigValidatorFactory;
|
||||||
|
this.accountValidatorFactory = accountValidatorFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validatePreMerge(
|
public void validatePreMerge(
|
||||||
@ -72,7 +82,8 @@ public class MergeValidators {
|
|||||||
List<MergeValidationListener> validators =
|
List<MergeValidationListener> validators =
|
||||||
ImmutableList.of(
|
ImmutableList.of(
|
||||||
new PluginMergeValidationListener(mergeValidationListeners),
|
new PluginMergeValidationListener(mergeValidationListeners),
|
||||||
projectConfigValidatorFactory.create());
|
projectConfigValidatorFactory.create(),
|
||||||
|
accountValidatorFactory.create());
|
||||||
|
|
||||||
for (MergeValidationListener validator : validators) {
|
for (MergeValidationListener validator : validators) {
|
||||||
validator.onPreMerge(repo, commit, destProject, destBranch, patchSetId, caller);
|
validator.onPreMerge(repo, commit, destProject, destBranch, patchSetId, caller);
|
||||||
@ -210,4 +221,59 @@ public class MergeValidators {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class AccountValidator implements MergeValidationListener {
|
||||||
|
public interface Factory {
|
||||||
|
AccountValidator create();
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Provider<ReviewDb> dbProvider;
|
||||||
|
private final AllUsersName allUsersName;
|
||||||
|
private final ChangeData.Factory changeDataFactory;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public AccountValidator(
|
||||||
|
Provider<ReviewDb> dbProvider,
|
||||||
|
AllUsersName allUsersName,
|
||||||
|
ChangeData.Factory changeDataFactory) {
|
||||||
|
this.dbProvider = dbProvider;
|
||||||
|
this.allUsersName = allUsersName;
|
||||||
|
this.changeDataFactory = changeDataFactory;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPreMerge(
|
||||||
|
Repository repo,
|
||||||
|
CodeReviewCommit commit,
|
||||||
|
ProjectState destProject,
|
||||||
|
Branch.NameKey destBranch,
|
||||||
|
PatchSet.Id patchSetId,
|
||||||
|
IdentifiedUser caller)
|
||||||
|
throws MergeValidationException {
|
||||||
|
if (!allUsersName.equals(destProject.getProject().getNameKey())
|
||||||
|
|| Account.Id.fromRef(destBranch.get()) == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commit.getParentCount() > 1) {
|
||||||
|
// for merge commits we cannot ensure that the 'account.config' file is not modified, since
|
||||||
|
// for merge commits file modifications that come in through the merge don't appear in the
|
||||||
|
// file list that is returned by ChangeData#currentFilePaths()
|
||||||
|
throw new MergeValidationException("cannot submit merge commit to user branch");
|
||||||
|
}
|
||||||
|
|
||||||
|
ChangeData cd =
|
||||||
|
changeDataFactory.create(
|
||||||
|
dbProvider.get(), destProject.getProject().getNameKey(), patchSetId.getParentKey());
|
||||||
|
try {
|
||||||
|
if (cd.currentFilePaths().contains(AccountConfig.ACCOUNT_CONFIG)) {
|
||||||
|
throw new MergeValidationException(
|
||||||
|
String.format("update of %s not allowed", AccountConfig.ACCOUNT_CONFIG));
|
||||||
|
}
|
||||||
|
} catch (OrmException e) {
|
||||||
|
log.error("Cannot validate account update", e);
|
||||||
|
throw new MergeValidationException("account validation unavailable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,7 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class AddMembers implements RestModifyView<GroupResource, Input> {
|
public class AddMembers implements RestModifyView<GroupResource, Input> {
|
||||||
@ -115,7 +116,7 @@ public class AddMembers implements RestModifyView<GroupResource, Input> {
|
|||||||
@Override
|
@Override
|
||||||
public List<AccountInfo> apply(GroupResource resource, Input input)
|
public List<AccountInfo> apply(GroupResource resource, Input input)
|
||||||
throws AuthException, MethodNotAllowedException, UnprocessableEntityException, OrmException,
|
throws AuthException, MethodNotAllowedException, UnprocessableEntityException, OrmException,
|
||||||
IOException {
|
IOException, ConfigInvalidException {
|
||||||
AccountGroup internalGroup = resource.toAccountGroup();
|
AccountGroup internalGroup = resource.toAccountGroup();
|
||||||
if (internalGroup == null) {
|
if (internalGroup == null) {
|
||||||
throw new MethodNotAllowedException();
|
throw new MethodNotAllowedException();
|
||||||
@ -143,7 +144,8 @@ public class AddMembers implements RestModifyView<GroupResource, Input> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Account findAccount(String nameOrEmailOrId)
|
Account findAccount(String nameOrEmailOrId)
|
||||||
throws AuthException, UnprocessableEntityException, OrmException, IOException {
|
throws AuthException, UnprocessableEntityException, OrmException, IOException,
|
||||||
|
ConfigInvalidException {
|
||||||
try {
|
try {
|
||||||
return accounts.parse(nameOrEmailOrId).getAccount();
|
return accounts.parse(nameOrEmailOrId).getAccount();
|
||||||
} catch (UnprocessableEntityException e) {
|
} catch (UnprocessableEntityException e) {
|
||||||
@ -235,7 +237,7 @@ public class AddMembers implements RestModifyView<GroupResource, Input> {
|
|||||||
@Override
|
@Override
|
||||||
public AccountInfo apply(GroupResource resource, PutMember.Input input)
|
public AccountInfo apply(GroupResource resource, PutMember.Input input)
|
||||||
throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException,
|
throws AuthException, MethodNotAllowedException, ResourceNotFoundException, OrmException,
|
||||||
IOException {
|
IOException, ConfigInvalidException {
|
||||||
AddMembers.Input in = new AddMembers.Input();
|
AddMembers.Input in = new AddMembers.Input();
|
||||||
in._oneMember = id;
|
in._oneMember = id;
|
||||||
try {
|
try {
|
||||||
|
@ -55,6 +55,7 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
import org.eclipse.jgit.lib.PersonIdent;
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
|
|
||||||
@ -115,7 +116,7 @@ public class CreateGroup implements RestModifyView<TopLevelResource, GroupInput>
|
|||||||
@Override
|
@Override
|
||||||
public GroupInfo apply(TopLevelResource resource, GroupInput input)
|
public GroupInfo apply(TopLevelResource resource, GroupInput input)
|
||||||
throws AuthException, BadRequestException, UnprocessableEntityException,
|
throws AuthException, BadRequestException, UnprocessableEntityException,
|
||||||
ResourceConflictException, OrmException, IOException {
|
ResourceConflictException, OrmException, IOException, ConfigInvalidException {
|
||||||
if (input == null) {
|
if (input == null) {
|
||||||
input = new GroupInput();
|
input = new GroupInput();
|
||||||
}
|
}
|
||||||
|
@ -38,6 +38,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class DeleteMembers implements RestModifyView<GroupResource, Input> {
|
public class DeleteMembers implements RestModifyView<GroupResource, Input> {
|
||||||
@ -64,7 +65,7 @@ public class DeleteMembers implements RestModifyView<GroupResource, Input> {
|
|||||||
@Override
|
@Override
|
||||||
public Response<?> apply(GroupResource resource, Input input)
|
public Response<?> apply(GroupResource resource, Input input)
|
||||||
throws AuthException, MethodNotAllowedException, UnprocessableEntityException, OrmException,
|
throws AuthException, MethodNotAllowedException, UnprocessableEntityException, OrmException,
|
||||||
IOException {
|
IOException, ConfigInvalidException {
|
||||||
AccountGroup internalGroup = resource.toAccountGroup();
|
AccountGroup internalGroup = resource.toAccountGroup();
|
||||||
if (internalGroup == null) {
|
if (internalGroup == null) {
|
||||||
throw new MethodNotAllowedException();
|
throw new MethodNotAllowedException();
|
||||||
@ -125,7 +126,7 @@ public class DeleteMembers implements RestModifyView<GroupResource, Input> {
|
|||||||
@Override
|
@Override
|
||||||
public Response<?> apply(MemberResource resource, Input input)
|
public Response<?> apply(MemberResource resource, Input input)
|
||||||
throws AuthException, MethodNotAllowedException, UnprocessableEntityException, OrmException,
|
throws AuthException, MethodNotAllowedException, UnprocessableEntityException, OrmException,
|
||||||
IOException {
|
IOException, ConfigInvalidException {
|
||||||
AddMembers.Input in = new AddMembers.Input();
|
AddMembers.Input in = new AddMembers.Input();
|
||||||
in._oneMember = resource.getMember().getAccountId().toString();
|
in._oneMember = resource.getMember().getAccountId().toString();
|
||||||
return delete.get().apply(resource, in);
|
return delete.get().apply(resource, in);
|
||||||
|
@ -32,6 +32,8 @@ import com.google.gwtorm.server.OrmException;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
public class MembersCollection
|
public class MembersCollection
|
||||||
@ -63,7 +65,8 @@ public class MembersCollection
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MemberResource parse(GroupResource parent, IdString id)
|
public MemberResource parse(GroupResource parent, IdString id)
|
||||||
throws MethodNotAllowedException, AuthException, ResourceNotFoundException, OrmException {
|
throws MethodNotAllowedException, AuthException, ResourceNotFoundException, OrmException,
|
||||||
|
IOException, ConfigInvalidException {
|
||||||
if (parent.toAccountGroup() == null) {
|
if (parent.toAccountGroup() == null) {
|
||||||
throw new MethodNotAllowedException();
|
throw new MethodNotAllowedException();
|
||||||
}
|
}
|
||||||
|
@ -732,7 +732,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> label(String name) throws QueryParseException, OrmException {
|
public Predicate<ChangeData> label(String name)
|
||||||
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
Set<Account.Id> accounts = null;
|
Set<Account.Id> accounts = null;
|
||||||
AccountGroup.UUID group = null;
|
AccountGroup.UUID group = null;
|
||||||
|
|
||||||
@ -846,7 +847,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> starredby(String who) throws QueryParseException, OrmException {
|
public Predicate<ChangeData> starredby(String who)
|
||||||
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
return starredby(parseAccount(who));
|
return starredby(parseAccount(who));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -863,7 +865,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> watchedby(String who) throws QueryParseException, OrmException {
|
public Predicate<ChangeData> watchedby(String who)
|
||||||
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
Set<Account.Id> m = parseAccount(who);
|
Set<Account.Id> m = parseAccount(who);
|
||||||
List<IsWatchedByPredicate> p = Lists.newArrayListWithCapacity(m.size());
|
List<IsWatchedByPredicate> p = Lists.newArrayListWithCapacity(m.size());
|
||||||
|
|
||||||
@ -887,7 +890,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> draftby(String who) throws QueryParseException, OrmException {
|
public Predicate<ChangeData> draftby(String who)
|
||||||
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
Set<Account.Id> m = parseAccount(who);
|
Set<Account.Id> m = parseAccount(who);
|
||||||
List<Predicate<ChangeData>> p = Lists.newArrayListWithCapacity(m.size());
|
List<Predicate<ChangeData>> p = Lists.newArrayListWithCapacity(m.size());
|
||||||
for (Account.Id id : m) {
|
for (Account.Id id : m) {
|
||||||
@ -905,7 +909,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> visibleto(String who) throws QueryParseException, OrmException {
|
public Predicate<ChangeData> visibleto(String who)
|
||||||
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
if (isSelf(who)) {
|
if (isSelf(who)) {
|
||||||
return is_visible();
|
return is_visible();
|
||||||
}
|
}
|
||||||
@ -942,12 +947,14 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> o(String who) throws QueryParseException, OrmException {
|
public Predicate<ChangeData> o(String who)
|
||||||
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
return owner(who);
|
return owner(who);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> owner(String who) throws QueryParseException, OrmException {
|
public Predicate<ChangeData> owner(String who)
|
||||||
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
return owner(parseAccount(who));
|
return owner(parseAccount(who));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -960,7 +967,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Predicate<ChangeData> ownerDefaultField(String who)
|
private Predicate<ChangeData> ownerDefaultField(String who)
|
||||||
throws QueryParseException, OrmException {
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
Set<Account.Id> accounts = parseAccount(who);
|
Set<Account.Id> accounts = parseAccount(who);
|
||||||
if (accounts.size() > MAX_ACCOUNTS_PER_DEFAULT_FIELD) {
|
if (accounts.size() > MAX_ACCOUNTS_PER_DEFAULT_FIELD) {
|
||||||
return Predicate.any();
|
return Predicate.any();
|
||||||
@ -969,7 +976,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> assignee(String who) throws QueryParseException, OrmException {
|
public Predicate<ChangeData> assignee(String who)
|
||||||
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
return assignee(parseAccount(who));
|
return assignee(parseAccount(who));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -991,22 +999,24 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> r(String who) throws QueryParseException, OrmException {
|
public Predicate<ChangeData> r(String who)
|
||||||
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
return reviewer(who);
|
return reviewer(who);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> reviewer(String who) throws QueryParseException, OrmException {
|
public Predicate<ChangeData> reviewer(String who)
|
||||||
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
return reviewer(who, false);
|
return reviewer(who, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Predicate<ChangeData> reviewerDefaultField(String who)
|
private Predicate<ChangeData> reviewerDefaultField(String who)
|
||||||
throws QueryParseException, OrmException {
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
return reviewer(who, true);
|
return reviewer(who, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Predicate<ChangeData> reviewer(String who, boolean forDefaultField)
|
private Predicate<ChangeData> reviewer(String who, boolean forDefaultField)
|
||||||
throws QueryParseException, OrmException {
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
Predicate<ChangeData> byState =
|
Predicate<ChangeData> byState =
|
||||||
reviewerByState(who, ReviewerStateInternal.REVIEWER, forDefaultField);
|
reviewerByState(who, ReviewerStateInternal.REVIEWER, forDefaultField);
|
||||||
if (Objects.equals(byState, Predicate.<ChangeData>any())) {
|
if (Objects.equals(byState, Predicate.<ChangeData>any())) {
|
||||||
@ -1020,7 +1030,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> cc(String who) throws QueryParseException, OrmException {
|
public Predicate<ChangeData> cc(String who)
|
||||||
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
return reviewerByState(who, ReviewerStateInternal.CC, false);
|
return reviewerByState(who, ReviewerStateInternal.CC, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1073,7 +1084,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> commentby(String who) throws QueryParseException, OrmException {
|
public Predicate<ChangeData> commentby(String who)
|
||||||
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
return commentby(parseAccount(who));
|
return commentby(parseAccount(who));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1086,7 +1098,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> from(String who) throws QueryParseException, OrmException {
|
public Predicate<ChangeData> from(String who)
|
||||||
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
Set<Account.Id> ownerIds = parseAccount(who);
|
Set<Account.Id> ownerIds = parseAccount(who);
|
||||||
return Predicate.or(owner(ownerIds), commentby(ownerIds));
|
return Predicate.or(owner(ownerIds), commentby(ownerIds));
|
||||||
}
|
}
|
||||||
@ -1110,7 +1123,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Operator
|
@Operator
|
||||||
public Predicate<ChangeData> reviewedby(String who) throws QueryParseException, OrmException {
|
public Predicate<ChangeData> reviewedby(String who)
|
||||||
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
return IsReviewedPredicate.create(parseAccount(who));
|
return IsReviewedPredicate.create(parseAccount(who));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1192,7 +1206,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
if (!Objects.equals(p, Predicate.<ChangeData>any())) {
|
if (!Objects.equals(p, Predicate.<ChangeData>any())) {
|
||||||
predicates.add(p);
|
predicates.add(p);
|
||||||
}
|
}
|
||||||
} catch (OrmException | QueryParseException e) {
|
} catch (OrmException | IOException | ConfigInvalidException | QueryParseException e) {
|
||||||
// Skip.
|
// Skip.
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -1200,13 +1214,13 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
if (!Objects.equals(p, Predicate.<ChangeData>any())) {
|
if (!Objects.equals(p, Predicate.<ChangeData>any())) {
|
||||||
predicates.add(p);
|
predicates.add(p);
|
||||||
}
|
}
|
||||||
} catch (OrmException | QueryParseException e) {
|
} catch (OrmException | IOException | ConfigInvalidException | QueryParseException e) {
|
||||||
// Skip.
|
// Skip.
|
||||||
}
|
}
|
||||||
predicates.add(file(query));
|
predicates.add(file(query));
|
||||||
try {
|
try {
|
||||||
predicates.add(label(query));
|
predicates.add(label(query));
|
||||||
} catch (OrmException | QueryParseException e) {
|
} catch (OrmException | IOException | ConfigInvalidException | QueryParseException e) {
|
||||||
// Skip.
|
// Skip.
|
||||||
}
|
}
|
||||||
predicates.add(commit(query));
|
predicates.add(commit(query));
|
||||||
@ -1245,7 +1259,8 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
return Predicate.and(predicates);
|
return Predicate.and(predicates);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<Account.Id> parseAccount(String who) throws QueryParseException, OrmException {
|
private Set<Account.Id> parseAccount(String who)
|
||||||
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
if (isSelf(who)) {
|
if (isSelf(who)) {
|
||||||
return Collections.singleton(self());
|
return Collections.singleton(self());
|
||||||
}
|
}
|
||||||
@ -1291,7 +1306,7 @@ public class ChangeQueryBuilder extends QueryBuilder<ChangeData> {
|
|||||||
|
|
||||||
public Predicate<ChangeData> reviewerByState(
|
public Predicate<ChangeData> reviewerByState(
|
||||||
String who, ReviewerStateInternal state, boolean forDefaultField)
|
String who, ReviewerStateInternal state, boolean forDefaultField)
|
||||||
throws QueryParseException, OrmException {
|
throws QueryParseException, OrmException, IOException, ConfigInvalidException {
|
||||||
Predicate<ChangeData> reviewerByEmailPredicate = null;
|
Predicate<ChangeData> reviewerByEmailPredicate = null;
|
||||||
if (args.index.getSchema().hasField(ChangeField.REVIEWER_BY_EMAIL)) {
|
if (args.index.getSchema().hasField(ChangeField.REVIEWER_BY_EMAIL)) {
|
||||||
Address address = Address.tryParse(who);
|
Address address = Address.tryParse(who);
|
||||||
|
@ -35,7 +35,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
/** A version of the database schema. */
|
/** A version of the database schema. */
|
||||||
public abstract class SchemaVersion {
|
public abstract class SchemaVersion {
|
||||||
/** The current schema version. */
|
/** The current schema version. */
|
||||||
public static final Class<Schema_153> C = Schema_153.class;
|
public static final Class<Schema_154> C = Schema_154.class;
|
||||||
|
|
||||||
public static int getBinaryVersion() {
|
public static int getBinaryVersion() {
|
||||||
return guessVersion(C);
|
return guessVersion(C);
|
||||||
|
@ -20,6 +20,7 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
|
|||||||
import com.google.gerrit.server.GerritPersonIdent;
|
import com.google.gerrit.server.GerritPersonIdent;
|
||||||
import com.google.gerrit.server.account.AccountsUpdate;
|
import com.google.gerrit.server.account.AccountsUpdate;
|
||||||
import com.google.gerrit.server.config.AllUsersName;
|
import com.google.gerrit.server.config.AllUsersName;
|
||||||
|
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
@ -88,7 +89,15 @@ public class Schema_146 extends SchemaVersion {
|
|||||||
rewriteUserBranch(repo, rw, oi, emptyTree, ref, e.getValue());
|
rewriteUserBranch(repo, rw, oi, emptyTree, ref, e.getValue());
|
||||||
} else {
|
} else {
|
||||||
AccountsUpdate.createUserBranch(
|
AccountsUpdate.createUserBranch(
|
||||||
repo, oi, serverIdent, serverIdent, e.getKey(), e.getValue());
|
repo,
|
||||||
|
allUsersName,
|
||||||
|
GitReferenceUpdated.DISABLED,
|
||||||
|
null,
|
||||||
|
oi,
|
||||||
|
serverIdent,
|
||||||
|
serverIdent,
|
||||||
|
e.getKey(),
|
||||||
|
e.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -22,6 +22,7 @@ import com.google.gerrit.reviewdb.server.ReviewDb;
|
|||||||
import com.google.gerrit.server.GerritPersonIdent;
|
import com.google.gerrit.server.GerritPersonIdent;
|
||||||
import com.google.gerrit.server.account.AccountsUpdate;
|
import com.google.gerrit.server.account.AccountsUpdate;
|
||||||
import com.google.gerrit.server.config.AllUsersName;
|
import com.google.gerrit.server.config.AllUsersName;
|
||||||
|
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
import com.google.gwtorm.server.OrmException;
|
import com.google.gwtorm.server.OrmException;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
@ -68,7 +69,8 @@ public class Schema_147 extends SchemaVersion {
|
|||||||
.collect(toSet());
|
.collect(toSet());
|
||||||
accountIdsFromUserBranches.removeAll(accountIdsFromReviewDb);
|
accountIdsFromUserBranches.removeAll(accountIdsFromReviewDb);
|
||||||
for (Account.Id accountId : accountIdsFromUserBranches) {
|
for (Account.Id accountId : accountIdsFromUserBranches) {
|
||||||
AccountsUpdate.deleteUserBranch(repo, serverIdent, accountId);
|
AccountsUpdate.deleteUserBranch(
|
||||||
|
repo, allUsersName, GitReferenceUpdated.DISABLED, null, serverIdent, accountId);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new OrmException("Failed to delete user branches for non-existing accounts.", e);
|
throw new OrmException("Failed to delete user branches for non-existing accounts.", e);
|
||||||
|
@ -0,0 +1,105 @@
|
|||||||
|
// Copyright (C) 2017 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.schema;
|
||||||
|
|
||||||
|
import com.google.gerrit.reviewdb.client.Account;
|
||||||
|
import com.google.gerrit.reviewdb.server.ReviewDb;
|
||||||
|
import com.google.gerrit.server.GerritPersonIdent;
|
||||||
|
import com.google.gerrit.server.account.AccountConfig;
|
||||||
|
import com.google.gerrit.server.config.AllUsersName;
|
||||||
|
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||||
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
|
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||||
|
import com.google.gwtorm.server.OrmException;
|
||||||
|
import com.google.inject.Inject;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.sql.ResultSet;
|
||||||
|
import java.sql.SQLException;
|
||||||
|
import java.sql.Statement;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
|
|
||||||
|
/** Migrate accounts to NoteDb. */
|
||||||
|
public class Schema_154 extends SchemaVersion {
|
||||||
|
private final GitRepositoryManager repoManager;
|
||||||
|
private final AllUsersName allUsersName;
|
||||||
|
private final Provider<PersonIdent> serverIdent;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
Schema_154(
|
||||||
|
Provider<Schema_153> prior,
|
||||||
|
GitRepositoryManager repoManager,
|
||||||
|
AllUsersName allUsersName,
|
||||||
|
@GerritPersonIdent Provider<PersonIdent> serverIdent) {
|
||||||
|
super(prior);
|
||||||
|
this.repoManager = repoManager;
|
||||||
|
this.allUsersName = allUsersName;
|
||||||
|
this.serverIdent = serverIdent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException {
|
||||||
|
try {
|
||||||
|
try (Repository repo = repoManager.openRepository(allUsersName)) {
|
||||||
|
for (Account account : scanAccounts(db)) {
|
||||||
|
updateAccountInNoteDb(repo, account);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException | ConfigInvalidException e) {
|
||||||
|
throw new OrmException("Migrating accounts to NoteDb failed", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<Account> scanAccounts(ReviewDb db) throws SQLException {
|
||||||
|
try (Statement stmt = newStatement(db);
|
||||||
|
ResultSet rs =
|
||||||
|
stmt.executeQuery(
|
||||||
|
"SELECT account_id,"
|
||||||
|
+ " registered_on,"
|
||||||
|
+ " full_name, "
|
||||||
|
+ " preferred_email,"
|
||||||
|
+ " status,"
|
||||||
|
+ " inactive"
|
||||||
|
+ " FROM accounts")) {
|
||||||
|
Set<Account> s = new HashSet<>();
|
||||||
|
while (rs.next()) {
|
||||||
|
Account a = new Account(new Account.Id(rs.getInt(1)), rs.getTimestamp(2));
|
||||||
|
a.setFullName(rs.getString(3));
|
||||||
|
a.setPreferredEmail(rs.getString(4));
|
||||||
|
a.setStatus(rs.getString(5));
|
||||||
|
a.setActive(rs.getString(6).equals("N"));
|
||||||
|
s.add(a);
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAccountInNoteDb(Repository allUsersRepo, Account account)
|
||||||
|
throws IOException, ConfigInvalidException {
|
||||||
|
MetaDataUpdate md =
|
||||||
|
new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsersName, allUsersRepo);
|
||||||
|
PersonIdent ident = serverIdent.get();
|
||||||
|
md.getCommitBuilder().setAuthor(ident);
|
||||||
|
md.getCommitBuilder().setCommitter(ident);
|
||||||
|
AccountConfig accountConfig = new AccountConfig(null, account.getId());
|
||||||
|
accountConfig.load(allUsersRepo);
|
||||||
|
accountConfig.setAccount(account);
|
||||||
|
accountConfig.commit(md);
|
||||||
|
}
|
||||||
|
}
|
@ -33,14 +33,20 @@ 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.AnonymousUser;
|
import com.google.gerrit.server.AnonymousUser;
|
||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
|
import com.google.gerrit.server.GerritPersonIdent;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.account.AccountCache;
|
import com.google.gerrit.server.account.AccountCache;
|
||||||
|
import com.google.gerrit.server.account.AccountConfig;
|
||||||
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.Accounts;
|
import com.google.gerrit.server.account.Accounts;
|
||||||
import com.google.gerrit.server.account.AccountsUpdate;
|
import com.google.gerrit.server.account.AccountsUpdate;
|
||||||
import com.google.gerrit.server.account.AuthRequest;
|
import com.google.gerrit.server.account.AuthRequest;
|
||||||
import com.google.gerrit.server.config.AllProjectsName;
|
import com.google.gerrit.server.config.AllProjectsName;
|
||||||
|
import com.google.gerrit.server.config.AllUsersName;
|
||||||
|
import com.google.gerrit.server.extensions.events.GitReferenceUpdated;
|
||||||
|
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||||
|
import com.google.gerrit.server.git.MetaDataUpdate;
|
||||||
import com.google.gerrit.server.schema.SchemaCreator;
|
import com.google.gerrit.server.schema.SchemaCreator;
|
||||||
import com.google.gerrit.server.util.ManualRequestContext;
|
import com.google.gerrit.server.util.ManualRequestContext;
|
||||||
import com.google.gerrit.server.util.OneOffRequestContext;
|
import com.google.gerrit.server.util.OneOffRequestContext;
|
||||||
@ -58,6 +64,8 @@ import java.util.Arrays;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.eclipse.jgit.lib.Config;
|
import org.eclipse.jgit.lib.Config;
|
||||||
|
import org.eclipse.jgit.lib.PersonIdent;
|
||||||
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
@ -82,6 +90,8 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
|
|||||||
|
|
||||||
@Inject protected GerritApi gApi;
|
@Inject protected GerritApi gApi;
|
||||||
|
|
||||||
|
@Inject @GerritPersonIdent Provider<PersonIdent> serverIdent;
|
||||||
|
|
||||||
@Inject protected IdentifiedUser.GenericFactory userFactory;
|
@Inject protected IdentifiedUser.GenericFactory userFactory;
|
||||||
|
|
||||||
@Inject private Provider<AnonymousUser> anonymousUser;
|
@Inject private Provider<AnonymousUser> anonymousUser;
|
||||||
@ -98,6 +108,10 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
|
|||||||
|
|
||||||
@Inject protected AllProjectsName allProjects;
|
@Inject protected AllProjectsName allProjects;
|
||||||
|
|
||||||
|
@Inject protected AllUsersName allUsers;
|
||||||
|
|
||||||
|
@Inject protected GitRepositoryManager repoManager;
|
||||||
|
|
||||||
protected LifecycleManager lifecycle;
|
protected LifecycleManager lifecycle;
|
||||||
protected Injector injector;
|
protected Injector injector;
|
||||||
protected ReviewDb db;
|
protected ReviewDb db;
|
||||||
@ -383,12 +397,25 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
|
|||||||
public void reindex() throws Exception {
|
public void reindex() throws Exception {
|
||||||
AccountInfo user1 = newAccountWithFullName("tester", "Test Usre");
|
AccountInfo user1 = newAccountWithFullName("tester", "Test Usre");
|
||||||
|
|
||||||
// update account in the database so that account index is stale
|
// update account in ReviewDb without reindex so that account index is stale
|
||||||
String newName = "Test User";
|
String newName = "Test User";
|
||||||
Account account = accounts.get(db, new Account.Id(user1._accountId));
|
Account.Id accountId = new Account.Id(user1._accountId);
|
||||||
|
Account account = accounts.get(db, accountId);
|
||||||
account.setFullName(newName);
|
account.setFullName(newName);
|
||||||
db.accounts().update(ImmutableSet.of(account));
|
db.accounts().update(ImmutableSet.of(account));
|
||||||
|
|
||||||
|
// update account in NoteDb without reindex so that account index is stale
|
||||||
|
try (Repository repo = repoManager.openRepository(allUsers)) {
|
||||||
|
MetaDataUpdate md = new MetaDataUpdate(GitReferenceUpdated.DISABLED, allUsers, repo);
|
||||||
|
PersonIdent ident = serverIdent.get();
|
||||||
|
md.getCommitBuilder().setAuthor(ident);
|
||||||
|
md.getCommitBuilder().setCommitter(ident);
|
||||||
|
AccountConfig accountConfig = new AccountConfig(null, accountId);
|
||||||
|
accountConfig.load(repo);
|
||||||
|
accountConfig.getAccount().setFullName(newName);
|
||||||
|
accountConfig.commit(md);
|
||||||
|
}
|
||||||
|
|
||||||
assertQuery("name:" + quote(user1.name), user1);
|
assertQuery("name:" + quote(user1.name), user1);
|
||||||
assertQuery("name:" + quote(newName));
|
assertQuery("name:" + quote(newName));
|
||||||
|
|
||||||
@ -469,11 +496,16 @@ public abstract class AbstractQueryAccountsTest extends GerritServerTests {
|
|||||||
if (email != null) {
|
if (email != null) {
|
||||||
accountManager.link(id, AuthRequest.forEmail(email));
|
accountManager.link(id, AuthRequest.forEmail(email));
|
||||||
}
|
}
|
||||||
Account a = accounts.get(db, id);
|
accountsUpdate
|
||||||
a.setFullName(fullName);
|
.create()
|
||||||
a.setPreferredEmail(email);
|
.update(
|
||||||
a.setActive(active);
|
db,
|
||||||
accountsUpdate.create().update(db, a);
|
id,
|
||||||
|
a -> {
|
||||||
|
a.setFullName(fullName);
|
||||||
|
a.setPreferredEmail(email);
|
||||||
|
a.setActive(active);
|
||||||
|
});
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,13 +208,11 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
|
|||||||
db = schemaFactory.open();
|
db = schemaFactory.open();
|
||||||
|
|
||||||
userId = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
|
userId = accountManager.authenticate(AuthRequest.forUser("user")).getAccountId();
|
||||||
Account userAccount = accounts.get(db, userId);
|
|
||||||
String email = "user@example.com";
|
String email = "user@example.com";
|
||||||
externalIdsUpdate.create().insert(ExternalId.createEmail(userId, email));
|
externalIdsUpdate.create().insert(ExternalId.createEmail(userId, email));
|
||||||
userAccount.setPreferredEmail(email);
|
accountsUpdate.create().update(db, userId, a -> a.setPreferredEmail(email));
|
||||||
accountsUpdate.create().update(db, userAccount);
|
|
||||||
user = userFactory.create(userId);
|
user = userFactory.create(userId);
|
||||||
requestContext.setContext(newRequestContext(userAccount.getId()));
|
requestContext.setContext(newRequestContext(userId));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected RequestContext newRequestContext(Account.Id requestUserId) {
|
protected RequestContext newRequestContext(Account.Id requestUserId) {
|
||||||
@ -2340,11 +2338,16 @@ public abstract class AbstractQueryChangesTest extends GerritServerTests {
|
|||||||
if (email != null) {
|
if (email != null) {
|
||||||
accountManager.link(id, AuthRequest.forEmail(email));
|
accountManager.link(id, AuthRequest.forEmail(email));
|
||||||
}
|
}
|
||||||
Account a = accounts.get(db, id);
|
accountsUpdate
|
||||||
a.setFullName(fullName);
|
.create()
|
||||||
a.setPreferredEmail(email);
|
.update(
|
||||||
a.setActive(active);
|
db,
|
||||||
accountsUpdate.create().update(db, a);
|
id,
|
||||||
|
a -> {
|
||||||
|
a.setFullName(fullName);
|
||||||
|
a.setPreferredEmail(email);
|
||||||
|
a.setActive(active);
|
||||||
|
});
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -319,11 +319,16 @@ public abstract class AbstractQueryGroupsTest extends GerritServerTests {
|
|||||||
if (email != null) {
|
if (email != null) {
|
||||||
accountManager.link(id, AuthRequest.forEmail(email));
|
accountManager.link(id, AuthRequest.forEmail(email));
|
||||||
}
|
}
|
||||||
Account a = accounts.get(db, id);
|
accountsUpdate
|
||||||
a.setFullName(fullName);
|
.create()
|
||||||
a.setPreferredEmail(email);
|
.update(
|
||||||
a.setActive(active);
|
db,
|
||||||
accountsUpdate.create().update(db, a);
|
id,
|
||||||
|
a -> {
|
||||||
|
a.setFullName(fullName);
|
||||||
|
a.setPreferredEmail(email);
|
||||||
|
a.setActive(active);
|
||||||
|
});
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ import com.google.inject.Inject;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.kohsuke.args4j.Argument;
|
import org.kohsuke.args4j.Argument;
|
||||||
import org.kohsuke.args4j.Option;
|
import org.kohsuke.args4j.Option;
|
||||||
|
|
||||||
@ -103,7 +104,7 @@ final class CreateGroupCommand extends SshCommand {
|
|||||||
@Inject private AddIncludedGroups addIncludedGroups;
|
@Inject private AddIncludedGroups addIncludedGroups;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run() throws Failure, OrmException, IOException {
|
protected void run() throws Failure, OrmException, IOException, ConfigInvalidException {
|
||||||
try {
|
try {
|
||||||
GroupResource rsrc = createGroup();
|
GroupResource rsrc = createGroup();
|
||||||
|
|
||||||
@ -119,7 +120,8 @@ final class CreateGroupCommand extends SshCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private GroupResource createGroup() throws RestApiException, OrmException, IOException {
|
private GroupResource createGroup()
|
||||||
|
throws RestApiException, OrmException, IOException, ConfigInvalidException {
|
||||||
GroupInput input = new GroupInput();
|
GroupInput input = new GroupInput();
|
||||||
input.description = groupDescription;
|
input.description = groupDescription;
|
||||||
input.visibleToAll = visibleToAll;
|
input.visibleToAll = visibleToAll;
|
||||||
@ -132,7 +134,8 @@ final class CreateGroupCommand extends SshCommand {
|
|||||||
return groups.parse(TopLevelResource.INSTANCE, IdString.fromUrl(group.id));
|
return groups.parse(TopLevelResource.INSTANCE, IdString.fromUrl(group.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addMembers(GroupResource rsrc) throws RestApiException, OrmException, IOException {
|
private void addMembers(GroupResource rsrc)
|
||||||
|
throws RestApiException, OrmException, IOException, ConfigInvalidException {
|
||||||
AddMembers.Input input =
|
AddMembers.Input input =
|
||||||
AddMembers.Input.fromMembers(
|
AddMembers.Input.fromMembers(
|
||||||
initialMembers.stream().map(Object::toString).collect(toList()));
|
initialMembers.stream().map(Object::toString).collect(toList()));
|
||||||
|
@ -35,6 +35,7 @@ import com.google.gwtorm.server.OrmException;
|
|||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.eclipse.jgit.errors.ConfigInvalidException;
|
||||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||||
import org.eclipse.jgit.lib.Ref;
|
import org.eclipse.jgit.lib.Ref;
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
@ -79,7 +80,7 @@ public class LsUserRefs extends SshCommand {
|
|||||||
Account userAccount;
|
Account userAccount;
|
||||||
try {
|
try {
|
||||||
userAccount = accountResolver.find(db, userName);
|
userAccount = accountResolver.find(db, userName);
|
||||||
} catch (OrmException e) {
|
} catch (OrmException | IOException | ConfigInvalidException e) {
|
||||||
throw die(e);
|
throw die(e);
|
||||||
}
|
}
|
||||||
if (userAccount == null) {
|
if (userAccount == null) {
|
||||||
|
@ -292,7 +292,8 @@ final class SetAccountCommand extends SshCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void putPreferred(String email)
|
private void putPreferred(String email)
|
||||||
throws RestApiException, OrmException, IOException, PermissionBackendException {
|
throws RestApiException, OrmException, IOException, PermissionBackendException,
|
||||||
|
ConfigInvalidException {
|
||||||
for (EmailInfo e : getEmails.apply(rsrc)) {
|
for (EmailInfo e : getEmails.apply(rsrc)) {
|
||||||
if (e.email.equals(email)) {
|
if (e.email.equals(email)) {
|
||||||
putPreferred.apply(new AccountResource.Email(user, email), null);
|
putPreferred.apply(new AccountResource.Email(user, email), null);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user