Move "ref_rights" table into Git

Permissions are stored in the project.config file within the
refs/meta/config branch of each project.  This makes the rules
more flexible in the future, as well as adds version control.

For example:

  [access "refs/*"]
    owner = group tools-owners

  [access "refs/heads/*"]
    label-Verified = -1..+1 group tools-dev
    label-Verified = -1..+1 group tools-owners
    label-Code-Review = -2..+2 group tools-owners
    submit = group tools-dev
    submit = group tools-owners

  [access "refs/heads/stable"]
    exclusiveGroupPermissions = read create push
    read = group Anonymous Users
    push = group tools-repo-maintainer

To enable easy remote editing of the configuration rules, the
following access block is added by default to -- All Projects --
and is thus inherited throughout the entire site:

  [access "refs/meta/config"]
    read = group Project Owners
    push = group Project Owners

This configuration section permits any project owner or site
administrator (as they are indirectly always a project owner of
any project) to push changes to the project.config file within
the refs/meta/config branch, updating access (and other project
information) remotely without using the web UI.

Change-Id: Idb56f657a4bf88108ad40bbb19d831e6806b68c5
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2011-01-05 12:46:21 -08:00
parent 83f6cc14af
commit 6a765190df
48 changed files with 2181 additions and 1628 deletions

View File

@@ -14,13 +14,16 @@
package com.google.gerrit.server.project;
import com.google.gerrit.common.data.AccessSection;
import com.google.gerrit.common.data.GroupReference;
import com.google.gerrit.common.data.Permission;
import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.AccountGroup;
import com.google.gerrit.reviewdb.ApprovalCategory;
import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.reviewdb.RefRight;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.WildProjectName;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@@ -28,15 +31,13 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
/** Cached information on a project. */
public class ProjectState {
public interface Factory {
ProjectState create(Project project, Collection<RefRight> localRights);
ProjectState create(ProjectConfig config);
}
private final AnonymousUser anonymousUser;
@@ -44,92 +45,64 @@ public class ProjectState {
private final ProjectCache projectCache;
private final ProjectControl.AssistedFactory projectControlFactory;
private final Project project;
private final Collection<RefRight> localRights;
private final ProjectConfig config;
private final Set<AccountGroup.UUID> localOwners;
private volatile Collection<RefRight> inheritedRights;
@Inject
protected ProjectState(final AnonymousUser anonymousUser,
final ProjectCache projectCache,
@WildProjectName final Project.NameKey wildProject,
final ProjectControl.AssistedFactory projectControlFactory,
@Assisted final Project project,
@Assisted Collection<RefRight> rights) {
@Assisted final ProjectConfig config) {
this.anonymousUser = anonymousUser;
this.projectCache = projectCache;
this.wildProject = wildProject;
this.projectControlFactory = projectControlFactory;
this.config = config;
if (wildProject.equals(project.getNameKey())) {
rights = new ArrayList<RefRight>(rights);
for (Iterator<RefRight> itr = rights.iterator(); itr.hasNext();) {
if (!itr.next().getApprovalCategoryId().canBeOnWildProject()) {
itr.remove();
HashSet<AccountGroup.UUID> groups = new HashSet<AccountGroup.UUID>();
AccessSection all = config.getAccessSection(AccessSection.ALL);
if (all != null) {
Permission owner = all.getPermission(Permission.OWNER);
if (owner != null) {
for (PermissionRule rule : owner.getRules()) {
GroupReference ref = rule.getGroup();
if (ref.getUUID() != null) {
groups.add(ref.getUUID());
}
}
}
rights = Collections.unmodifiableCollection(rights);
}
this.project = project;
this.localRights = rights;
final HashSet<AccountGroup.UUID> groups = new HashSet<AccountGroup.UUID>();
for (final RefRight right : rights) {
if (ApprovalCategory.OWN.equals(right.getApprovalCategoryId())
&& right.getMaxValue() > 0
&& right.getRefPattern().equals(RefRight.ALL)) {
groups.add(right.getAccountGroupUUID());
}
}
localOwners = Collections.unmodifiableSet(groups);
}
public Project getProject() {
return project;
return getConfig().getProject();
}
public ProjectConfig getConfig() {
return config;
}
/** Get the rights that pertain only to this project. */
public Collection<RefRight> getLocalRights() {
return localRights;
public Collection<AccessSection> getLocalAccessSections() {
return getConfig().getAccessSections();
}
/**
* Get the rights that pertain only to this project.
*
* @param action the category requested.
* @return immutable collection of rights for the requested category.
*/
public Collection<RefRight> getLocalRights(ApprovalCategory.Id action) {
return filter(getLocalRights(), action);
}
/** Get the rights this project inherits from the wild project. */
public Collection<RefRight> getInheritedRights() {
if (inheritedRights == null) {
inheritedRights = computeInheritedRights();
}
return inheritedRights;
}
void setInheritedRights(Collection<RefRight> all) {
inheritedRights = all;
}
private Collection<RefRight> computeInheritedRights() {
if (isSpecialWildProject()) {
/** Get the rights this project inherits. */
public Collection<AccessSection> getInheritedAccessSections() {
if (isWildProject()) {
return Collections.emptyList();
}
List<RefRight> inherited = new ArrayList<RefRight>();
List<AccessSection> inherited = new ArrayList<AccessSection>();
Set<Project.NameKey> seen = new HashSet<Project.NameKey>();
Project.NameKey parent = project.getParent();
Project.NameKey parent = getProject().getParent();
while (parent != null && seen.add(parent)) {
ProjectState s = projectCache.get(parent);
if (s != null) {
inherited.addAll(s.getLocalRights());
inherited.addAll(s.getLocalAccessSections());
parent = s.getProject().getParent();
} else {
break;
@@ -138,76 +111,21 @@ public class ProjectState {
// Wild project is the parent, or the root of the tree
if (parent == null) {
inherited.addAll(getWildProjectRights());
}
return Collections.unmodifiableCollection(inherited);
}
private Collection<RefRight> getWildProjectRights() {
final ProjectState s = projectCache.get(wildProject);
return s != null ? s.getLocalRights() : Collections.<RefRight> emptyList();
}
/**
* Utility class that is needed to filter overridden refrights
*/
private static class Grant {
final AccountGroup.Id group;
final String pattern;
private Grant(AccountGroup.Id group, String pattern) {
this.group = group;
this.pattern = pattern;
}
@Override
public boolean equals(Object o) {
if (o == null)
return false;
Grant grant = (Grant) o;
return group.equals(grant.group) && pattern.equals(grant.pattern);
}
@Override
public int hashCode() {
int result = group.hashCode();
result = 31 * result + pattern.hashCode();
return result;
}
}
/**
* Get the rights this project has and inherits from the wild project.
*
* @param action the category requested.
* @param dropOverridden whether to remove inherited permissions in case if we have a
* local one that matches (action,group,ref)
* @return immutable collection of rights for the requested category.
*/
public Collection<RefRight> getAllRights(ApprovalCategory.Id action, boolean dropOverridden) {
Collection<RefRight> rights = new LinkedList<RefRight>(getLocalRights(action));
rights.addAll(filter(getInheritedRights(), action));
if (dropOverridden) {
Set<Grant> grants = new HashSet<Grant>();
Iterator<RefRight> iter = rights.iterator();
while (iter.hasNext()) {
RefRight right = iter.next();
Grant grant = new Grant(right.getAccountGroupId(), right.getRefPattern());
if (grants.contains(grant)) {
iter.remove();
} else {
grants.add(grant);
}
ProjectState s = projectCache.get(wildProject);
if (s != null) {
inherited.addAll(s.getLocalAccessSections());
}
}
return Collections.unmodifiableCollection(rights);
return inherited;
}
/** Is this the special wild project which manages inherited rights? */
public boolean isSpecialWildProject() {
return project.getNameKey().equals(wildProject);
/** Get both local and inherited access sections. */
public Collection<AccessSection> getAllAccessSections() {
List<AccessSection> all = new ArrayList<AccessSection>();
all.addAll(getLocalAccessSections());
all.addAll(getInheritedAccessSections());
return all;
}
/**
@@ -217,12 +135,12 @@ public class ProjectState {
* that has local owners are returned
*/
public Set<AccountGroup.UUID> getOwners() {
if (!localOwners.isEmpty() || isSpecialWildProject()
|| project.getParent() == null) {
Project.NameKey parentName = getProject().getParent();
if (!localOwners.isEmpty() || parentName == null || isWildProject()) {
return localOwners;
}
final ProjectState parent = projectCache.get(project.getParent());
ProjectState parent = projectCache.get(parentName);
if (parent != null) {
return parent.getOwners();
}
@@ -238,12 +156,22 @@ public class ProjectState {
* assigned for one of the parent projects (the inherited owners).
*/
public Set<AccountGroup.UUID> getAllOwners() {
final HashSet<AccountGroup.UUID> owners = new HashSet<AccountGroup.UUID>();
for (final RefRight right : getAllRights(ApprovalCategory.OWN, true)) {
if (right.getMaxValue() > 0 && right.getRefPattern().equals(RefRight.ALL)) {
owners.add(right.getAccountGroupUUID());
HashSet<AccountGroup.UUID> owners = new HashSet<AccountGroup.UUID>();
owners.addAll(localOwners);
Set<Project.NameKey> seen = new HashSet<Project.NameKey>();
Project.NameKey parent = getProject().getParent();
while (parent != null && seen.add(parent)) {
ProjectState s = projectCache.get(parent);
if (s != null) {
owners.addAll(s.localOwners);
parent = s.getProject().getParent();
} else {
break;
}
}
return Collections.unmodifiableSet(owners);
}
@@ -255,20 +183,7 @@ public class ProjectState {
return projectControlFactory.create(user, this);
}
private static Collection<RefRight> filter(Collection<RefRight> all,
ApprovalCategory.Id actionId) {
if (all.isEmpty()) {
return Collections.emptyList();
}
final Collection<RefRight> mine = new ArrayList<RefRight>(all.size());
for (final RefRight right : all) {
if (right.getApprovalCategoryId().equals(actionId)) {
mine.add(right);
}
}
if (mine.isEmpty()) {
return Collections.emptyList();
}
return Collections.unmodifiableCollection(mine);
private boolean isWildProject() {
return wildProject.equals(getProject().getNameKey());
}
}