Merge changes from topic 'refs-users-self'

* changes:
  RefPatternMatcher: Pass in CurrentUser instead of username
  Support push to refs/users/self in All-Users repository
  Support fetch of refs/users/self from All-Users repository
This commit is contained in:
David Pursehouse
2016-06-01 00:11:37 +00:00
committed by Gerrit Code Review
11 changed files with 191 additions and 56 deletions

View File

@@ -17,6 +17,7 @@ package com.google.gerrit.acceptance.api.accounts;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assert_;
import static com.google.gerrit.acceptance.GitUtil.fetch;
import static com.google.gerrit.gpg.PublicKeyStore.REFS_GPG_KEYS;
import static com.google.gerrit.gpg.PublicKeyStore.keyToString;
import static com.google.gerrit.gpg.testutil.TestKeys.allValidKeys;
@@ -36,9 +37,11 @@ import com.google.gerrit.acceptance.AbstractDaemonTest;
import com.google.gerrit.acceptance.AccountCreator;
import com.google.gerrit.acceptance.PushOneCommit;
import com.google.gerrit.acceptance.TestAccount;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.extensions.api.accounts.EmailInput;
import com.google.gerrit.extensions.api.changes.AddReviewerInput;
import com.google.gerrit.extensions.api.changes.StarsInput;
import com.google.gerrit.extensions.client.GeneralPreferencesInfo;
import com.google.gerrit.extensions.common.AccountInfo;
import com.google.gerrit.extensions.common.ChangeInfo;
import com.google.gerrit.extensions.common.GpgKeyInfo;
@@ -53,7 +56,10 @@ import com.google.gerrit.gpg.server.GpgKeys;
import com.google.gerrit.gpg.testutil.TestKey;
import com.google.gerrit.reviewdb.client.Account;
import com.google.gerrit.reviewdb.client.AccountExternalId;
import com.google.gerrit.reviewdb.client.RefNames;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.util.MagicBranch;
import com.google.gerrit.testutil.ConfigSuite;
import com.google.gerrit.testutil.FakeEmailSender.Message;
import com.google.inject.Inject;
@@ -62,6 +68,8 @@ import com.google.inject.Provider;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.eclipse.jgit.internal.storage.dfs.InMemoryRepository;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefUpdate;
@@ -354,6 +362,84 @@ public class AccountIT extends AbstractDaemonTest {
gApi.accounts().self().addEmail(input);
}
@Test
public void fetchUserBranch() throws Exception {
// change something in the user preferences to ensure that the user branch
// is created
GeneralPreferencesInfo input = new GeneralPreferencesInfo();
input.changesPerPage =
GeneralPreferencesInfo.defaults().changesPerPage + 10;
gApi.accounts().self().setPreferences(input);
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
Ref userRef = allUsersRepo.getRepository().exactRef("userRef");
assertThat(userRef).isNotNull();
fetch(allUsersRepo, RefNames.REFS_USERS_SELF + ":userSelfRef");
Ref userSelfRef =
allUsersRepo.getRepository().getRefDatabase().exactRef("userSelfRef");
assertThat(userSelfRef).isNotNull();
assertThat(userSelfRef.getObjectId()).isEqualTo(userRef.getObjectId());
}
@Test
public void pushToUserBranch() throws Exception {
// change something in the user preferences to ensure that the user branch
// is created
GeneralPreferencesInfo input = new GeneralPreferencesInfo();
input.changesPerPage =
GeneralPreferencesInfo.defaults().changesPerPage + 10;
gApi.accounts().self().setPreferences(input);
removeExclusiveReadPermissionOnAllUsers();
String userRefName = RefNames.refsUsers(admin.id);
grant(Permission.PUSH, allUsers, userRefName);
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
allUsersRepo.reset("userRef");
PushOneCommit push = pushFactory.create(db, admin.getIdent(), allUsersRepo);
push.to(userRefName).assertOkStatus();
push = pushFactory.create(db, admin.getIdent(), allUsersRepo);
push.to(RefNames.REFS_USERS_SELF).assertOkStatus();
}
@Test
public void pushToUserBranchForReview() throws Exception {
// change something in the user preferences to ensure that the user branch
// is created
GeneralPreferencesInfo input = new GeneralPreferencesInfo();
input.changesPerPage =
GeneralPreferencesInfo.defaults().changesPerPage + 10;
gApi.accounts().self().setPreferences(input);
removeExclusiveReadPermissionOnAllUsers();
String userRefName = RefNames.refsUsers(admin.id);
grant(Permission.PUSH, allUsers, MagicBranch.NEW_CHANGE + userRefName);
TestRepository<InMemoryRepository> allUsersRepo = cloneProject(allUsers);
fetch(allUsersRepo, RefNames.refsUsers(admin.id) + ":userRef");
allUsersRepo.reset("userRef");
PushOneCommit push = pushFactory.create(db, admin.getIdent(), allUsersRepo);
PushOneCommit.Result r = push.to(MagicBranch.NEW_CHANGE + userRefName);
r.assertOkStatus();
assertThat(r.getChange().change().getDest().get()).isEqualTo(userRefName);
push = pushFactory.create(db, admin.getIdent(), allUsersRepo);
r = push.to(MagicBranch.NEW_CHANGE + RefNames.REFS_USERS_SELF);
r.assertOkStatus();
assertThat(r.getChange().change().getDest().get()).isEqualTo(userRefName);
}
private void removeExclusiveReadPermissionOnAllUsers() throws Exception {
ProjectConfig cfg = projectCache.checkedGet(allUsers).getConfig();
cfg.getAccessSection(RefNames.REFS_USERS + "*", true)
.remove(new Permission(Permission.READ));
saveProjectConfig(allUsers, cfg);
}
@Test
public void addGpgKey() throws Exception {
TestKey key = validKeyWithoutExpiration();

View File

@@ -33,6 +33,9 @@ public class RefNames {
/** Preference settings for a user {@code refs/users} */
public static final String REFS_USERS = "refs/users/";
/** Magic user branch in All-Users {@code refs/users/self} */
public static final String REFS_USERS_SELF = "refs/users/self";
/** Default user preference settings */
public static final String REFS_USERS_DEFAULT = RefNames.REFS_USERS + "default";

View File

@@ -632,7 +632,7 @@ public class ReceiveCommits {
}
Set<Branch.NameKey> branches = new HashSet<>();
for (ReceiveCommand c : commands) {
for (ReceiveCommand c : batch.getCommands()) {
if (c.getResult() == OK) {
if (c.getType() == ReceiveCommand.Type.UPDATE) { // aka fast-forward
tagCache.updateFastForward(project.getNameKey(),
@@ -890,6 +890,19 @@ public class ReceiveCommits {
continue;
}
if (projectControl.getProjectState().isAllUsers()
&& RefNames.REFS_USERS_SELF.equals(cmd.getRefName())) {
final ReceiveCommand orgCmd = cmd;
cmd = new ReceiveCommand(cmd.getOldId(), cmd.getNewId(),
RefNames.refsUsers(user.getAccountId()), cmd.getType()) {
@Override
public void setResult(Result s, String m) {
super.setResult(s, m);
orgCmd.setResult(s, m);
}
};
}
Matcher m = NEW_PATCHSET.matcher(cmd.getRefName());
if (m.matches()) {
// The referenced change must exist and must still be open.
@@ -1303,6 +1316,10 @@ public class ReceiveCommits {
reject(cmd, "see help");
return;
}
if (projectControl.getProjectState().isAllUsers()
&& RefNames.REFS_USERS_SELF.equals(ref)) {
ref = RefNames.refsUsers(user.getAccountId());
}
if (!rp.getAdvertisedRefs().containsKey(ref) && !ref.equals(readHEAD(repo))) {
if (ref.startsWith(Constants.R_HEADS)) {
String n = ref.substring(Constants.R_HEADS.length());

View File

@@ -29,6 +29,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.RefDatabase;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.SymbolicRef;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.AbstractAdvertiseRefsHook;
import org.eclipse.jgit.transport.ServiceMayNotContinueException;
@@ -69,6 +70,18 @@ public class VisibleRefFilter extends AbstractAdvertiseRefsHook {
}
public Map<String, Ref> filter(Map<String, Ref> refs, boolean filterTagsSeparately) {
if (projectCtl.getProjectState().isAllUsers()
&& projectCtl.getUser().isIdentifiedUser()) {
Ref userRef =
refs.get(RefNames.refsUsers(projectCtl.getUser().getAccountId()));
if (userRef != null) {
SymbolicRef refsUsersSelf =
new SymbolicRef(RefNames.REFS_USERS_SELF, userRef);
refs = new HashMap<>(refs);
refs.put(refsUsersSelf.getName(), refsUsersSelf);
}
}
if (projectCtl.allRefsAreVisible(ImmutableSet.of(RefNames.REFS_CONFIG))) {
Map<String, Ref> r = Maps.newHashMap(refs);
if (!projectCtl.controlForRef(RefNames.REFS_CONFIG).isVisible()) {
@@ -97,7 +110,8 @@ public class VisibleRefFilter extends AbstractAdvertiseRefsHook {
Account.Id accountId;
if (ref.getName().startsWith(RefNames.REFS_CACHE_AUTOMERGE)) {
continue;
} else if ((accountId = Account.Id.fromRef(ref.getName())) != null) {
} else if ((accountId =
Account.Id.fromRef(ref.getLeaf().getName())) != null) {
// Reference related to an account is visible only for the current
// account.
//

View File

@@ -413,7 +413,7 @@ public class ChangeControl {
private boolean match(String destBranch, String refPattern) {
return RefPatternMatcher.getMatcher(refPattern).match(destBranch,
getUser().getUserName());
getUser());
}
private ChangeData changeData(ReviewDb db, @Nullable ChangeData cd) {

View File

@@ -26,12 +26,11 @@ import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -65,22 +64,21 @@ public class PermissionCollection {
* priority order (project specific definitions must appear before
* inherited ones).
* @param ref reference being accessed.
* @param usernameProvider if the reference is a per-user reference, access
* sections using the parameter variable "${username}" will first
* have each of {@code usernames} inserted into them before seeing if
* they apply to the reference named by {@code ref}.
* @param user if the reference is a per-user reference, e.g. access
* sections using the parameter variable "${username}" will have
* each username inserted into them to see if they apply to the
* reference named by {@code ref}.
* @return map of permissions that apply to this reference, keyed by
* permission name.
*/
PermissionCollection filter(Iterable<SectionMatcher> matcherList,
String ref, Provider<? extends Collection<String>> usernameProvider) {
String ref, CurrentUser user) {
if (isRE(ref)) {
ref = RefControl.shortestExample(ref);
} else if (ref.endsWith("/*")) {
ref = ref.substring(0, ref.length() - 1);
}
Collection<String> usernames = null;
boolean perUser = false;
Map<AccessSection, Project.NameKey> sectionToProject = new LinkedHashMap<>();
for (SectionMatcher sm : matcherList) {
@@ -101,15 +99,10 @@ public class PermissionCollection {
continue;
}
perUser = true;
if (usernames == null) {
usernames = usernameProvider.get();
}
for (String username : usernames) {
if (sm.match(ref, username)) {
if (sm.match(ref, user)) {
sectionToProject.put(sm.section, sm.project);
break;
}
}
} else if (sm.match(ref, null)) {
sectionToProject.put(sm.section, sm.project);
}

View File

@@ -227,25 +227,8 @@ public class ProjectControl {
}
RefControl ctl = refControls.get(refName);
if (ctl == null) {
Provider<List<String>> usernames = new Provider<List<String>>() {
@Override
public List<String> get() {
List<String> r;
if (user.isIdentifiedUser()) {
Set<String> emails = user.asIdentifiedUser().getEmailAddresses();
r = new ArrayList<>(emails.size() + 1);
r.addAll(emails);
} else {
r = new ArrayList<>(1);
}
if (user.getUserName() != null) {
r.add(user.getUserName());
}
return r;
}
};
PermissionCollection relevant =
permissionFilter.filter(access(), refName, usernames);
permissionFilter.filter(access(), refName, user);
ctl = new RefControl(this, refName, relevant);
refControls.put(refName, ctl);
}

View File

@@ -38,6 +38,7 @@ import com.google.gerrit.rules.RulesCache;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.account.CapabilityCollection;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.BranchOrderSection;
import com.google.gerrit.server.git.GitRepositoryManager;
@@ -80,6 +81,7 @@ public class ProjectState {
}
private final boolean isAllProjects;
private final boolean isAllUsers;
private final SitePaths sitePaths;
private final AllProjectsName allProjectsName;
private final ProjectCache projectCache;
@@ -113,6 +115,7 @@ public class ProjectState {
final SitePaths sitePaths,
final ProjectCache projectCache,
final AllProjectsName allProjectsName,
final AllUsersName allUsersName,
final ProjectControl.AssistedFactory projectControlFactory,
final PrologEnvironment.Factory envFactory,
final GitRepositoryManager gitMgr,
@@ -122,6 +125,7 @@ public class ProjectState {
this.sitePaths = sitePaths;
this.projectCache = projectCache;
this.isAllProjects = config.getProject().getNameKey().equals(allProjectsName);
this.isAllUsers = config.getProject().getNameKey().equals(allUsersName);
this.allProjectsName = allProjectsName;
this.projectControlFactory = projectControlFactory;
this.envFactory = envFactory;
@@ -362,6 +366,10 @@ public class ProjectState {
return isAllProjects;
}
public boolean isAllUsers() {
return isAllUsers;
}
public boolean isUseContributorAgreements() {
return getInheritableBoolean(new Function<Project, InheritableBoolean>() {
@Override

View File

@@ -16,11 +16,15 @@ package com.google.gerrit.server.project;
import static com.google.gerrit.server.project.RefControl.isRE;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.gerrit.common.data.ParameterizedString;
import com.google.gerrit.server.CurrentUser;
import dk.brics.automaton.Automaton;
import java.util.Collections;
import java.util.Set;
import java.util.regex.Pattern;
public abstract class RefPatternMatcher {
@@ -36,7 +40,7 @@ public abstract class RefPatternMatcher {
}
}
public abstract boolean match(String ref, String username);
public abstract boolean match(String ref, CurrentUser user);
private static class Exact extends RefPatternMatcher {
private final String expect;
@@ -46,7 +50,7 @@ public abstract class RefPatternMatcher {
}
@Override
public boolean match(String ref, String username) {
public boolean match(String ref, CurrentUser user) {
return expect.equals(ref);
}
}
@@ -59,7 +63,7 @@ public abstract class RefPatternMatcher {
}
@Override
public boolean match(String ref, String username) {
public boolean match(String ref, CurrentUser user) {
return ref.startsWith(prefix);
}
}
@@ -72,7 +76,7 @@ public abstract class RefPatternMatcher {
}
@Override
public boolean match(String ref, String username) {
public boolean match(String ref, CurrentUser user) {
return pattern.matcher(ref).matches();
}
}
@@ -101,11 +105,12 @@ public abstract class RefPatternMatcher {
}
@Override
public boolean match(String ref, String username) {
if (!ref.startsWith(prefix) || username == null) {
public boolean match(String ref, CurrentUser user) {
if (!ref.startsWith(prefix)) {
return false;
}
for (String username : getUsernames(user)) {
String u;
if (isRE(template.getPattern())) {
u = Pattern.quote(username);
@@ -114,7 +119,27 @@ public abstract class RefPatternMatcher {
}
RefPatternMatcher next = getMatcher(expand(template, u));
return next != null ? next.match(expand(ref, u), username) : false;
if (next != null && next.match(expand(ref, u), user)) {
return true;
}
}
return false;
}
private Iterable<String> getUsernames(CurrentUser user) {
if (user.isIdentifiedUser()) {
Set<String> emails = user.asIdentifiedUser().getEmailAddresses();
if (user.getUserName() == null) {
return emails;
} else if (emails.isEmpty()) {
return ImmutableSet.of(user.getUserName());
}
Iterables.concat(emails, ImmutableSet.of(user.getUserName()));
}
if (user.getUserName() != null) {
return ImmutableSet.of(user.getUserName());
}
return ImmutableSet.of();
}
boolean matchPrefix(String ref) {

View File

@@ -16,6 +16,7 @@ package com.google.gerrit.server.project;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
/**
* Matches an AccessSection against a reference name.
@@ -45,7 +46,7 @@ class SectionMatcher extends RefPatternMatcher {
}
@Override
public boolean match(String ref, String username) {
return this.matcher.match(ref, username);
public boolean match(String ref, CurrentUser user) {
return this.matcher.match(ref, user);
}
}

View File

@@ -51,6 +51,8 @@ import com.google.gerrit.server.account.GroupMembership;
import com.google.gerrit.server.account.ListGroupMembership;
import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllProjectsNameProvider;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.config.AllUsersNameProvider;
import com.google.gerrit.server.config.SitePaths;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.gerrit.server.schema.SchemaCreator;
@@ -218,6 +220,8 @@ public class RefControlTest {
private final AllProjectsName allProjectsName =
new AllProjectsName(AllProjectsNameProvider.DEFAULT);
private final AllUsersName allUsersName =
new AllUsersName(AllUsersNameProvider.DEFAULT);
private final AccountGroup.UUID fixers = new AccountGroup.UUID("test.fixers");
private final Map<Project.NameKey, ProjectState> all = new HashMap<>();
private Project.NameKey localKey = new Project.NameKey("local");
@@ -858,9 +862,10 @@ public class RefControlTest {
} catch (IOException | ConfigInvalidException e) {
throw new RuntimeException(e);
}
all.put(pc.getName(), new ProjectState(sitePaths,
projectCache, allProjectsName, projectControlFactory, envFactory,
repoManager, rulesCache, commentLinks, pc));
all.put(pc.getName(),
new ProjectState(sitePaths, projectCache, allProjectsName, allUsersName,
projectControlFactory, envFactory, repoManager, rulesCache,
commentLinks, pc));
return repo;
}