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:
		@@ -0,0 +1,129 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2010 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.common.data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.HashSet;
 | 
				
			||||||
 | 
					import java.util.Iterator;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Portion of a {@link Project} describing access rules. */
 | 
				
			||||||
 | 
					public class AccessSection implements Comparable<AccessSection> {
 | 
				
			||||||
 | 
					  /** Pattern that matches all references in a project. */
 | 
				
			||||||
 | 
					  public static final String ALL = "refs/*";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Pattern that matches all branches in a project. */
 | 
				
			||||||
 | 
					  public static final String HEADS = "refs/heads/*";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Prefix that triggers a regular expression pattern. */
 | 
				
			||||||
 | 
					  public static final String REGEX_PREFIX = "^";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** @return true if the name is likely to be a valid access section name. */
 | 
				
			||||||
 | 
					  public static boolean isAccessSection(String name) {
 | 
				
			||||||
 | 
					    return name.startsWith("refs/") || name.startsWith("^refs/");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected String refPattern;
 | 
				
			||||||
 | 
					  protected List<Permission> permissions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected AccessSection() {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public AccessSection(String refPattern) {
 | 
				
			||||||
 | 
					    setRefPattern(refPattern);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public String getRefPattern() {
 | 
				
			||||||
 | 
					    return refPattern;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setRefPattern(String refPattern) {
 | 
				
			||||||
 | 
					    this.refPattern = refPattern;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public List<Permission> getPermissions() {
 | 
				
			||||||
 | 
					    if (permissions == null) {
 | 
				
			||||||
 | 
					      permissions = new ArrayList<Permission>();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return permissions;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setPermissions(List<Permission> list) {
 | 
				
			||||||
 | 
					    Set<String> names = new HashSet<String>();
 | 
				
			||||||
 | 
					    for (Permission p : list) {
 | 
				
			||||||
 | 
					      if (!names.add(p.getName().toLowerCase())) {
 | 
				
			||||||
 | 
					        throw new IllegalArgumentException();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    permissions = list;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public Permission getPermission(String name) {
 | 
				
			||||||
 | 
					    return getPermission(name, false);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public Permission getPermission(String name, boolean create) {
 | 
				
			||||||
 | 
					    for (Permission p : getPermissions()) {
 | 
				
			||||||
 | 
					      if (p.getName().equalsIgnoreCase(name)) {
 | 
				
			||||||
 | 
					        return p;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (create) {
 | 
				
			||||||
 | 
					      Permission p = new Permission(name);
 | 
				
			||||||
 | 
					      permissions.add(p);
 | 
				
			||||||
 | 
					      return p;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void remove(Permission permission) {
 | 
				
			||||||
 | 
					    if (permission != null) {
 | 
				
			||||||
 | 
					      removePermission(permission.getName());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void removePermission(String name) {
 | 
				
			||||||
 | 
					    if (permissions != null) {
 | 
				
			||||||
 | 
					      for (Iterator<Permission> itr = permissions.iterator(); itr.hasNext();) {
 | 
				
			||||||
 | 
					        if (name.equalsIgnoreCase(itr.next().getName())) {
 | 
				
			||||||
 | 
					          itr.remove();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public int compareTo(AccessSection o) {
 | 
				
			||||||
 | 
					    return comparePattern().compareTo(o.comparePattern());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private String comparePattern() {
 | 
				
			||||||
 | 
					    if (getRefPattern().startsWith(REGEX_PREFIX)) {
 | 
				
			||||||
 | 
					      return getRefPattern().substring(REGEX_PREFIX.length());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return getRefPattern();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public String toString() {
 | 
				
			||||||
 | 
					    return "AccessSection[" + getRefPattern() + "]";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -31,6 +31,7 @@ public class ApprovalType {
 | 
				
			|||||||
  protected short maxNegative;
 | 
					  protected short maxNegative;
 | 
				
			||||||
  protected short maxPositive;
 | 
					  protected short maxPositive;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private transient List<Integer> intList;
 | 
				
			||||||
  private transient Map<Short, ApprovalCategoryValue> byValue;
 | 
					  private transient Map<Short, ApprovalCategoryValue> byValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  protected ApprovalType() {
 | 
					  protected ApprovalType() {
 | 
				
			||||||
@@ -56,6 +57,9 @@ public class ApprovalType {
 | 
				
			|||||||
        maxPositive = values.get(values.size() - 1).getValue();
 | 
					        maxPositive = values.get(values.size() - 1).getValue();
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Force the label name to pre-compute so we don't have data race conditions.
 | 
				
			||||||
 | 
					    getCategory().getLabelName();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public ApprovalCategory getCategory() {
 | 
					  public ApprovalCategory getCategory() {
 | 
				
			||||||
@@ -107,4 +111,16 @@ public class ApprovalType {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public List<Integer> getValuesAsList() {
 | 
				
			||||||
 | 
					    if (intList == null) {
 | 
				
			||||||
 | 
					      intList = new ArrayList<Integer>(values.size());
 | 
				
			||||||
 | 
					      for (ApprovalCategoryValue acv : values) {
 | 
				
			||||||
 | 
					        intList.add(Integer.valueOf(acv.getValue()));
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      Collections.sort(intList);
 | 
				
			||||||
 | 
					      Collections.reverse(intList);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return intList;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,20 +19,17 @@ import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
				
			|||||||
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 java.util.Set;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class ApprovalTypes {
 | 
					public class ApprovalTypes {
 | 
				
			||||||
  protected List<ApprovalType> approvalTypes;
 | 
					  protected List<ApprovalType> approvalTypes;
 | 
				
			||||||
  protected List<ApprovalType> actionTypes;
 | 
					  private transient Map<ApprovalCategory.Id, ApprovalType> byId;
 | 
				
			||||||
  private transient Map<ApprovalCategory.Id, ApprovalType> byCategoryId;
 | 
					  private transient Map<String, ApprovalType> byLabel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  protected ApprovalTypes() {
 | 
					  protected ApprovalTypes() {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public ApprovalTypes(final List<ApprovalType> approvals,
 | 
					  public ApprovalTypes(final List<ApprovalType> approvals) {
 | 
				
			||||||
      final List<ApprovalType> actions) {
 | 
					 | 
				
			||||||
    approvalTypes = approvals;
 | 
					    approvalTypes = approvals;
 | 
				
			||||||
    actionTypes = actions;
 | 
					 | 
				
			||||||
    byCategory();
 | 
					    byCategory();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -40,33 +37,35 @@ public class ApprovalTypes {
 | 
				
			|||||||
    return approvalTypes;
 | 
					    return approvalTypes;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public List<ApprovalType> getActionTypes() {
 | 
					  public ApprovalType byId(final ApprovalCategory.Id id) {
 | 
				
			||||||
    return actionTypes;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public ApprovalType getApprovalType(final ApprovalCategory.Id id) {
 | 
					 | 
				
			||||||
    return byCategory().get(id);
 | 
					    return byCategory().get(id);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public Set<ApprovalCategory.Id> getApprovalCategories() {
 | 
					 | 
				
			||||||
    return byCategory().keySet();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private Map<ApprovalCategory.Id, ApprovalType> byCategory() {
 | 
					  private Map<ApprovalCategory.Id, ApprovalType> byCategory() {
 | 
				
			||||||
    if (byCategoryId == null) {
 | 
					    if (byId == null) {
 | 
				
			||||||
      byCategoryId = new HashMap<ApprovalCategory.Id, ApprovalType>();
 | 
					      byId = new HashMap<ApprovalCategory.Id, ApprovalType>();
 | 
				
			||||||
      if (actionTypes != null) {
 | 
					 | 
				
			||||||
        for (final ApprovalType t : actionTypes) {
 | 
					 | 
				
			||||||
          byCategoryId.put(t.getCategory().getId(), t);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      if (approvalTypes != null) {
 | 
					      if (approvalTypes != null) {
 | 
				
			||||||
        for (final ApprovalType t : approvalTypes) {
 | 
					        for (final ApprovalType t : approvalTypes) {
 | 
				
			||||||
          byCategoryId.put(t.getCategory().getId(), t);
 | 
					          byId.put(t.getCategory().getId(), t);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return byCategoryId;
 | 
					    return byId;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public ApprovalType byLabel(String labelName) {
 | 
				
			||||||
 | 
					    return byLabel().get(labelName.toLowerCase());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private Map<String, ApprovalType> byLabel() {
 | 
				
			||||||
 | 
					    if (byLabel == null) {
 | 
				
			||||||
 | 
					      byLabel = new HashMap<String, ApprovalType>();
 | 
				
			||||||
 | 
					      if (approvalTypes != null) {
 | 
				
			||||||
 | 
					        for (ApprovalType t : approvalTypes) {
 | 
				
			||||||
 | 
					          byLabel.put(t.getCategory().getLabelName().toLowerCase(), t);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return byLabel;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,10 +39,10 @@ public class ChangeDetail {
 | 
				
			|||||||
  protected List<PatchSet> patchSets;
 | 
					  protected List<PatchSet> patchSets;
 | 
				
			||||||
  protected List<ApprovalDetail> approvals;
 | 
					  protected List<ApprovalDetail> approvals;
 | 
				
			||||||
  protected Set<ApprovalCategory.Id> missingApprovals;
 | 
					  protected Set<ApprovalCategory.Id> missingApprovals;
 | 
				
			||||||
 | 
					  protected boolean canSubmit;
 | 
				
			||||||
  protected List<ChangeMessage> messages;
 | 
					  protected List<ChangeMessage> messages;
 | 
				
			||||||
  protected PatchSet.Id currentPatchSetId;
 | 
					  protected PatchSet.Id currentPatchSetId;
 | 
				
			||||||
  protected PatchSetDetail currentDetail;
 | 
					  protected PatchSetDetail currentDetail;
 | 
				
			||||||
  protected Set<ApprovalCategory.Id> currentActions;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public ChangeDetail() {
 | 
					  public ChangeDetail() {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -87,6 +87,14 @@ public class ChangeDetail {
 | 
				
			|||||||
      canRevert = a;
 | 
					      canRevert = a;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public boolean canSubmit() {
 | 
				
			||||||
 | 
					    return canSubmit;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setCanSubmit(boolean a) {
 | 
				
			||||||
 | 
					    canSubmit = a;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public Change getChange() {
 | 
					  public Change getChange() {
 | 
				
			||||||
    return change;
 | 
					    return change;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -153,14 +161,6 @@ public class ChangeDetail {
 | 
				
			|||||||
    missingApprovals = a;
 | 
					    missingApprovals = a;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public Set<ApprovalCategory.Id> getCurrentActions() {
 | 
					 | 
				
			||||||
    return currentActions;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public void setCurrentActions(Set<ApprovalCategory.Id> a) {
 | 
					 | 
				
			||||||
    currentActions = a;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public boolean isCurrentPatchSet(final PatchSetDetail detail) {
 | 
					  public boolean isCurrentPatchSet(final PatchSetDetail detail) {
 | 
				
			||||||
    return currentPatchSetId != null
 | 
					    return currentPatchSetId != null
 | 
				
			||||||
        && detail.getPatchSet().getId().equals(currentPatchSetId);
 | 
					        && detail.getPatchSet().getId().equals(currentPatchSetId);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2010 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.common.data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.reviewdb.AccountGroup;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Describes a group within a projects {@link AccessSection}s. */
 | 
				
			||||||
 | 
					public class GroupReference implements Comparable<GroupReference> {
 | 
				
			||||||
 | 
					  /** @return a new reference to the given group description. */
 | 
				
			||||||
 | 
					  public static GroupReference forGroup(AccountGroup group) {
 | 
				
			||||||
 | 
					    return new GroupReference(group.getGroupUUID(), group.getName());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected String uuid;
 | 
				
			||||||
 | 
					  protected String name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected GroupReference() {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public GroupReference(AccountGroup.UUID uuid, String name) {
 | 
				
			||||||
 | 
					    setUUID(uuid);
 | 
				
			||||||
 | 
					    setName(name);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public AccountGroup.UUID getUUID() {
 | 
				
			||||||
 | 
					    return uuid != null ? new AccountGroup.UUID(uuid) : null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setUUID(AccountGroup.UUID newUUID) {
 | 
				
			||||||
 | 
					    uuid = newUUID.get();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public String getName() {
 | 
				
			||||||
 | 
					    return name;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setName(String newName) {
 | 
				
			||||||
 | 
					    this.name = newName;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public int compareTo(GroupReference o) {
 | 
				
			||||||
 | 
					    return uuid(this).compareTo(uuid(o));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static String uuid(GroupReference a) {
 | 
				
			||||||
 | 
					    return a.getUUID() != null ? a.getUUID().get() : "?";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public int hashCode() {
 | 
				
			||||||
 | 
					    return uuid(this).hashCode();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public boolean equals(Object o) {
 | 
				
			||||||
 | 
					    return o instanceof GroupReference && compareTo((GroupReference) o) == 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public String toString() {
 | 
				
			||||||
 | 
					    return "Group[" + getName() + " / " + getUUID() + "]";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -15,39 +15,35 @@
 | 
				
			|||||||
package com.google.gerrit.common.data;
 | 
					package com.google.gerrit.common.data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
					import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.Change;
 | 
					import com.google.gerrit.reviewdb.Change;
 | 
				
			||||||
import com.google.gerrit.reviewdb.PatchLineComment;
 | 
					import com.google.gerrit.reviewdb.PatchLineComment;
 | 
				
			||||||
import com.google.gerrit.reviewdb.PatchSetApproval;
 | 
					import com.google.gerrit.reviewdb.PatchSetApproval;
 | 
				
			||||||
import com.google.gerrit.reviewdb.PatchSetInfo;
 | 
					import com.google.gerrit.reviewdb.PatchSetInfo;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
import java.util.Set;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class PatchSetPublishDetail {
 | 
					public class PatchSetPublishDetail {
 | 
				
			||||||
  protected AccountInfoCache accounts;
 | 
					  protected AccountInfoCache accounts;
 | 
				
			||||||
  protected PatchSetInfo patchSetInfo;
 | 
					  protected PatchSetInfo patchSetInfo;
 | 
				
			||||||
  protected Change change;
 | 
					  protected Change change;
 | 
				
			||||||
  protected List<PatchLineComment> drafts;
 | 
					  protected List<PatchLineComment> drafts;
 | 
				
			||||||
  protected Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> allowed;
 | 
					  protected List<PermissionRange> labels;
 | 
				
			||||||
  protected Map<ApprovalCategory.Id, PatchSetApproval> given;
 | 
					  protected List<PatchSetApproval> given;
 | 
				
			||||||
  protected boolean isSubmitAllowed;
 | 
					  protected boolean canSubmit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> getAllowed() {
 | 
					  public List<PermissionRange> getLabels() {
 | 
				
			||||||
    return allowed;
 | 
					    return labels;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void setAllowed(
 | 
					  public void setLabels(List<PermissionRange> labels) {
 | 
				
			||||||
      Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> allowed) {
 | 
					    this.labels = labels;
 | 
				
			||||||
    this.allowed = allowed;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public Map<ApprovalCategory.Id, PatchSetApproval> getGiven() {
 | 
					  public List<PatchSetApproval> getGiven() {
 | 
				
			||||||
    return given;
 | 
					    return given;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void setGiven(Map<ApprovalCategory.Id, PatchSetApproval> given) {
 | 
					  public void setGiven(List<PatchSetApproval> given) {
 | 
				
			||||||
    this.given = given;
 | 
					    this.given = given;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -67,8 +63,8 @@ public class PatchSetPublishDetail {
 | 
				
			|||||||
    this.drafts = drafts;
 | 
					    this.drafts = drafts;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void setSubmitAllowed(boolean allowed) {
 | 
					  public void setCanSubmit(boolean allowed) {
 | 
				
			||||||
    isSubmitAllowed = allowed;
 | 
					    canSubmit = allowed;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public AccountInfoCache getAccounts() {
 | 
					  public AccountInfoCache getAccounts() {
 | 
				
			||||||
@@ -87,20 +83,25 @@ public class PatchSetPublishDetail {
 | 
				
			|||||||
    return drafts;
 | 
					    return drafts;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public boolean isAllowed(final ApprovalCategory.Id id) {
 | 
					  public PermissionRange getRange(final String permissionName) {
 | 
				
			||||||
    final Set<ApprovalCategoryValue.Id> s = getAllowed(id);
 | 
					    for (PermissionRange s : labels) {
 | 
				
			||||||
    return s != null && !s.isEmpty();
 | 
					      if (s.getName().equals(permissionName)) {
 | 
				
			||||||
 | 
					        return s;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public Set<ApprovalCategoryValue.Id> getAllowed(final ApprovalCategory.Id id) {
 | 
					  public PatchSetApproval getChangeApproval(ApprovalCategory.Id id) {
 | 
				
			||||||
    return allowed.get(id);
 | 
					    for (PatchSetApproval a : given) {
 | 
				
			||||||
 | 
					      if (a.getCategoryId().equals(id)) {
 | 
				
			||||||
 | 
					        return a;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public PatchSetApproval getChangeApproval(final ApprovalCategory.Id id) {
 | 
					  public boolean canSubmit() {
 | 
				
			||||||
    return given.get(id);
 | 
					    return canSubmit;
 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public boolean isSubmitAllowed() {
 | 
					 | 
				
			||||||
    return isSubmitAllowed;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,190 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2010 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.common.data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.Iterator;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** A single permission within an {@link AccessSection} of a project. */
 | 
				
			||||||
 | 
					public class Permission implements Comparable<Permission> {
 | 
				
			||||||
 | 
					  public static final String CREATE = "create";
 | 
				
			||||||
 | 
					  public static final String FORGE_AUTHOR = "forgeAuthor";
 | 
				
			||||||
 | 
					  public static final String FORGE_COMMITTER = "forgeCommitter";
 | 
				
			||||||
 | 
					  public static final String FORGE_SERVER = "forgeServerAsCommitter";
 | 
				
			||||||
 | 
					  public static final String LABEL = "label-";
 | 
				
			||||||
 | 
					  public static final String OWNER = "owner";
 | 
				
			||||||
 | 
					  public static final String PUSH = "push";
 | 
				
			||||||
 | 
					  public static final String PUSH_MERGE = "pushMerge";
 | 
				
			||||||
 | 
					  public static final String PUSH_TAG = "pushTag";
 | 
				
			||||||
 | 
					  public static final String READ = "read";
 | 
				
			||||||
 | 
					  public static final String SUBMIT = "submit";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static final List<String> NAMES_LC;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static {
 | 
				
			||||||
 | 
					    NAMES_LC = new ArrayList<String>();
 | 
				
			||||||
 | 
					    NAMES_LC.add(OWNER.toLowerCase());
 | 
				
			||||||
 | 
					    NAMES_LC.add(READ.toLowerCase());
 | 
				
			||||||
 | 
					    NAMES_LC.add(CREATE.toLowerCase());
 | 
				
			||||||
 | 
					    NAMES_LC.add(FORGE_AUTHOR.toLowerCase());
 | 
				
			||||||
 | 
					    NAMES_LC.add(FORGE_COMMITTER.toLowerCase());
 | 
				
			||||||
 | 
					    NAMES_LC.add(FORGE_SERVER.toLowerCase());
 | 
				
			||||||
 | 
					    NAMES_LC.add(PUSH.toLowerCase());
 | 
				
			||||||
 | 
					    NAMES_LC.add(PUSH_MERGE.toLowerCase());
 | 
				
			||||||
 | 
					    NAMES_LC.add(PUSH_TAG.toLowerCase());
 | 
				
			||||||
 | 
					    NAMES_LC.add(LABEL.toLowerCase());
 | 
				
			||||||
 | 
					    NAMES_LC.add(SUBMIT.toLowerCase());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** @return true if the name is recognized as a permission name. */
 | 
				
			||||||
 | 
					  public static boolean isPermission(String varName) {
 | 
				
			||||||
 | 
					    String lc = varName.toLowerCase();
 | 
				
			||||||
 | 
					    if (lc.startsWith(LABEL)) {
 | 
				
			||||||
 | 
					      return LABEL.length() < lc.length();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return NAMES_LC.contains(lc);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** @return true if the permission name is actually for a review label. */
 | 
				
			||||||
 | 
					  public static boolean isLabel(String varName) {
 | 
				
			||||||
 | 
					    return varName.startsWith(LABEL) && LABEL.length() < varName.length();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** @return permission name for the given review label. */
 | 
				
			||||||
 | 
					  public static String forLabel(String labelName) {
 | 
				
			||||||
 | 
					    return LABEL + labelName;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected String name;
 | 
				
			||||||
 | 
					  protected boolean exclusiveGroup;
 | 
				
			||||||
 | 
					  protected List<PermissionRule> rules;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected Permission() {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public Permission(String name) {
 | 
				
			||||||
 | 
					    this.name = name;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public String getName() {
 | 
				
			||||||
 | 
					    return name;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public boolean isLabel() {
 | 
				
			||||||
 | 
					    return isLabel(getName());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public String getLabel() {
 | 
				
			||||||
 | 
					    if (isLabel()) {
 | 
				
			||||||
 | 
					      return getName().substring(LABEL.length());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public boolean getExclusiveGroup() {
 | 
				
			||||||
 | 
					    // Only permit exclusive group behavior on non OWNER permissions,
 | 
				
			||||||
 | 
					    // otherwise an owner might lose access to a delegated subspace.
 | 
				
			||||||
 | 
					    //
 | 
				
			||||||
 | 
					    return exclusiveGroup && !OWNER.equals(getName());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setExclusiveGroup(boolean newExclusiveGroup) {
 | 
				
			||||||
 | 
					    exclusiveGroup = newExclusiveGroup;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public List<PermissionRule> getRules() {
 | 
				
			||||||
 | 
					    initRules();
 | 
				
			||||||
 | 
					    return rules;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setRules(List<PermissionRule> list) {
 | 
				
			||||||
 | 
					    rules = list;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void add(PermissionRule rule) {
 | 
				
			||||||
 | 
					    initRules();
 | 
				
			||||||
 | 
					    rules.add(rule);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void remove(PermissionRule rule) {
 | 
				
			||||||
 | 
					    if (rule != null) {
 | 
				
			||||||
 | 
					      removeRule(rule.getGroup());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void removeRule(GroupReference group) {
 | 
				
			||||||
 | 
					    if (rules != null) {
 | 
				
			||||||
 | 
					      for (Iterator<PermissionRule> itr = rules.iterator(); itr.hasNext();) {
 | 
				
			||||||
 | 
					        if (sameGroup(itr.next(), group)) {
 | 
				
			||||||
 | 
					          itr.remove();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public PermissionRule getRule(GroupReference group) {
 | 
				
			||||||
 | 
					    return getRule(group, false);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public PermissionRule getRule(GroupReference group, boolean create) {
 | 
				
			||||||
 | 
					    initRules();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (PermissionRule r : rules) {
 | 
				
			||||||
 | 
					      if (sameGroup(r, group)) {
 | 
				
			||||||
 | 
					        return r;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (create) {
 | 
				
			||||||
 | 
					      PermissionRule r = new PermissionRule(group);
 | 
				
			||||||
 | 
					      rules.add(r);
 | 
				
			||||||
 | 
					      return r;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static boolean sameGroup(PermissionRule rule, GroupReference group) {
 | 
				
			||||||
 | 
					    if (group.getUUID() != null) {
 | 
				
			||||||
 | 
					      return group.getUUID().equals(rule.getGroup().getUUID());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else if (group.getName() != null) {
 | 
				
			||||||
 | 
					      return group.getName().equals(rule.getGroup().getName());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void initRules() {
 | 
				
			||||||
 | 
					    if (rules == null) {
 | 
				
			||||||
 | 
					      rules = new ArrayList<PermissionRule>(4);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public int compareTo(Permission b) {
 | 
				
			||||||
 | 
					    int cmp = index(this) - index(b);
 | 
				
			||||||
 | 
					    if (cmp == 0) getName().compareTo(b.getName());
 | 
				
			||||||
 | 
					    return cmp;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static int index(Permission a) {
 | 
				
			||||||
 | 
					    String lc = a.isLabel() ? Permission.LABEL : a.getName().toLowerCase();
 | 
				
			||||||
 | 
					    int index = NAMES_LC.indexOf(lc);
 | 
				
			||||||
 | 
					    return 0 <= index ? index : NAMES_LC.size();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,95 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2010 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.common.data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class PermissionRange implements Comparable<PermissionRange> {
 | 
				
			||||||
 | 
					  protected String name;
 | 
				
			||||||
 | 
					  protected int min;
 | 
				
			||||||
 | 
					  protected int max;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  protected PermissionRange() {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public PermissionRange(String name, int min, int max) {
 | 
				
			||||||
 | 
					    this.name = name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (min <= max) {
 | 
				
			||||||
 | 
					      this.min = min;
 | 
				
			||||||
 | 
					      this.max = max;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      this.min = max;
 | 
				
			||||||
 | 
					      this.max = min;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public String getName() {
 | 
				
			||||||
 | 
					    return name;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public boolean isLabel() {
 | 
				
			||||||
 | 
					    return Permission.isLabel(getName());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public String getLabel() {
 | 
				
			||||||
 | 
					    return isLabel() ? getName().substring(Permission.LABEL.length()) : null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public int getMin() {
 | 
				
			||||||
 | 
					    return min;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public int getMax() {
 | 
				
			||||||
 | 
					    return max;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** True if the value is within the range. */
 | 
				
			||||||
 | 
					  public boolean contains(int value) {
 | 
				
			||||||
 | 
					    return getMin() <= value && value <= getMax();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Normalize the value to fit within the bounds of the range. */
 | 
				
			||||||
 | 
					  public int squash(int value) {
 | 
				
			||||||
 | 
					    return Math.min(Math.max(getMin(), value), getMax());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** True both {@link #getMin()} and {@link #getMax()} are 0. */
 | 
				
			||||||
 | 
					  public boolean isEmpty() {
 | 
				
			||||||
 | 
					    return getMin() == 0 && getMax() == 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public int compareTo(PermissionRange o) {
 | 
				
			||||||
 | 
					    return getName().compareTo(o.getName());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public String toString() {
 | 
				
			||||||
 | 
					    StringBuilder r = new StringBuilder();
 | 
				
			||||||
 | 
					    if (getMin() < 0 && getMax() == 0) {
 | 
				
			||||||
 | 
					      r.append(getMin());
 | 
				
			||||||
 | 
					      r.append(' ');
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      if (getMin() != getMax()) {
 | 
				
			||||||
 | 
					        if (0 <= getMin()) r.append('+');
 | 
				
			||||||
 | 
					        r.append(getMin());
 | 
				
			||||||
 | 
					        r.append("..");
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (0 <= getMax()) r.append('+');
 | 
				
			||||||
 | 
					      r.append(getMax());
 | 
				
			||||||
 | 
					      r.append(' ');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return r.toString();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -0,0 +1,182 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2010 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.common.data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class PermissionRule implements Comparable<PermissionRule> {
 | 
				
			||||||
 | 
					  protected boolean deny;
 | 
				
			||||||
 | 
					  protected boolean force;
 | 
				
			||||||
 | 
					  protected int min;
 | 
				
			||||||
 | 
					  protected int max;
 | 
				
			||||||
 | 
					  protected GroupReference group;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public PermissionRule() {
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public PermissionRule(GroupReference group) {
 | 
				
			||||||
 | 
					    this.group = group;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public boolean getDeny() {
 | 
				
			||||||
 | 
					    return deny;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setDeny(boolean newDeny) {
 | 
				
			||||||
 | 
					    deny = newDeny;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public boolean getForce() {
 | 
				
			||||||
 | 
					    return force;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setForce(boolean newForce) {
 | 
				
			||||||
 | 
					    force = newForce;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public Integer getMin() {
 | 
				
			||||||
 | 
					    return min;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setMin(Integer min) {
 | 
				
			||||||
 | 
					    this.min = min;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setMax(Integer max) {
 | 
				
			||||||
 | 
					    this.max = max;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public Integer getMax() {
 | 
				
			||||||
 | 
					    return max;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setRange(int newMin, int newMax) {
 | 
				
			||||||
 | 
					    if (newMax < newMin) {
 | 
				
			||||||
 | 
					      min = newMax;
 | 
				
			||||||
 | 
					      max = newMin;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      min = newMin;
 | 
				
			||||||
 | 
					      max = newMax;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public GroupReference getGroup() {
 | 
				
			||||||
 | 
					    return group;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void setGroup(GroupReference newGroup) {
 | 
				
			||||||
 | 
					    group = newGroup;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public int compareTo(PermissionRule o) {
 | 
				
			||||||
 | 
					    int cmp = deny(this) - deny(o);
 | 
				
			||||||
 | 
					    if (cmp == 0) cmp = range(o) - range(this);
 | 
				
			||||||
 | 
					    if (cmp == 0) cmp = group(this).compareTo(group(o));
 | 
				
			||||||
 | 
					    return cmp;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static int deny(PermissionRule a) {
 | 
				
			||||||
 | 
					    return a.getDeny() ? 1 : 0;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static int range(PermissionRule a) {
 | 
				
			||||||
 | 
					    return Math.abs(a.getMin()) + Math.abs(a.getMax());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static String group(PermissionRule a) {
 | 
				
			||||||
 | 
					    return a.getGroup().getName() != null ? a.getGroup().getName() : "";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  public String toString() {
 | 
				
			||||||
 | 
					    return asString(true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public String asString(boolean canUseRange) {
 | 
				
			||||||
 | 
					    StringBuilder r = new StringBuilder();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (getDeny()) {
 | 
				
			||||||
 | 
					      r.append("deny ");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (getForce()) {
 | 
				
			||||||
 | 
					      r.append("+force ");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (canUseRange && (getMin() != 0 || getMax() != 0)) {
 | 
				
			||||||
 | 
					      if (0 <= getMin()) r.append('+');
 | 
				
			||||||
 | 
					      r.append(getMin());
 | 
				
			||||||
 | 
					      r.append("..");
 | 
				
			||||||
 | 
					      if (0 <= getMax()) r.append('+');
 | 
				
			||||||
 | 
					      r.append(getMax());
 | 
				
			||||||
 | 
					      r.append(' ');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    r.append("group ");
 | 
				
			||||||
 | 
					    r.append(getGroup().getName());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return r.toString();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public static PermissionRule fromString(String src, boolean mightUseRange) {
 | 
				
			||||||
 | 
					    final String orig = src;
 | 
				
			||||||
 | 
					    final PermissionRule rule = new PermissionRule();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    src = src.trim();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (src.startsWith("deny ")) {
 | 
				
			||||||
 | 
					      rule.setDeny(true);
 | 
				
			||||||
 | 
					      src = src.substring(5).trim();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (src.startsWith("+force ")) {
 | 
				
			||||||
 | 
					      rule.setForce(true);
 | 
				
			||||||
 | 
					      src = src.substring("+force ".length()).trim();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (mightUseRange && !src.startsWith("group ")) {
 | 
				
			||||||
 | 
					      int sp = src.indexOf(' ');
 | 
				
			||||||
 | 
					      String range = src.substring(0, sp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (range.matches("^([+-]\\d+)\\.\\.([+-]\\d)$")) {
 | 
				
			||||||
 | 
					        int dotdot = range.indexOf("..");
 | 
				
			||||||
 | 
					        int min = parseInt(range.substring(0, dotdot));
 | 
				
			||||||
 | 
					        int max = parseInt(range.substring(dotdot + 2));
 | 
				
			||||||
 | 
					        rule.setRange(min, max);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        throw new IllegalArgumentException("Invalid range in rule: " + orig);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      src = src.substring(sp + 1).trim();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (src.startsWith("group ")) {
 | 
				
			||||||
 | 
					      src = src.substring(6).trim();
 | 
				
			||||||
 | 
					      GroupReference group = new GroupReference();
 | 
				
			||||||
 | 
					      group.setName(src);
 | 
				
			||||||
 | 
					      rule.setGroup(group);
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      throw new IllegalArgumentException("Rule must include group: " + orig);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return rule;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static int parseInt(String value) {
 | 
				
			||||||
 | 
					    if (value.startsWith("+")) {
 | 
				
			||||||
 | 
					      value = value.substring(1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return Integer.parseInt(value);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -28,6 +28,8 @@ import com.google.gerrit.reviewdb.Account;
 | 
				
			|||||||
import com.google.gerrit.reviewdb.AccountDiffPreference;
 | 
					import com.google.gerrit.reviewdb.AccountDiffPreference;
 | 
				
			||||||
import com.google.gerrit.reviewdb.AccountGeneralPreferences;
 | 
					import com.google.gerrit.reviewdb.AccountGeneralPreferences;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
					import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
				
			||||||
 | 
					import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadCommand;
 | 
				
			||||||
 | 
					import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadScheme;
 | 
				
			||||||
import com.google.gerrit.reviewdb.Change;
 | 
					import com.google.gerrit.reviewdb.Change;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ChangeMessage;
 | 
					import com.google.gerrit.reviewdb.ChangeMessage;
 | 
				
			||||||
import com.google.gerrit.reviewdb.Patch;
 | 
					import com.google.gerrit.reviewdb.Patch;
 | 
				
			||||||
@@ -35,8 +37,6 @@ import com.google.gerrit.reviewdb.PatchSet;
 | 
				
			|||||||
import com.google.gerrit.reviewdb.PatchSetInfo;
 | 
					import com.google.gerrit.reviewdb.PatchSetInfo;
 | 
				
			||||||
import com.google.gerrit.reviewdb.Project;
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
import com.google.gerrit.reviewdb.UserIdentity;
 | 
					import com.google.gerrit.reviewdb.UserIdentity;
 | 
				
			||||||
import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadCommand;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadScheme;
 | 
					 | 
				
			||||||
import com.google.gwt.core.client.GWT;
 | 
					import com.google.gwt.core.client.GWT;
 | 
				
			||||||
import com.google.gwt.event.dom.client.ClickEvent;
 | 
					import com.google.gwt.event.dom.client.ClickEvent;
 | 
				
			||||||
import com.google.gwt.event.dom.client.ClickHandler;
 | 
					import com.google.gwt.event.dom.client.ClickHandler;
 | 
				
			||||||
@@ -54,7 +54,6 @@ import com.google.gwt.user.client.ui.Panel;
 | 
				
			|||||||
import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
 | 
					import com.google.gwt.user.client.ui.HTMLTable.CellFormatter;
 | 
				
			||||||
import com.google.gwtexpui.clippy.client.CopyableLabel;
 | 
					import com.google.gwtexpui.clippy.client.CopyableLabel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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;
 | 
				
			||||||
@@ -401,12 +400,8 @@ class PatchSetComplexDisclosurePanel extends ComplexDisclosurePanel implements O
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private void populateActions(final PatchSetDetail detail) {
 | 
					  private void populateActions(final PatchSetDetail detail) {
 | 
				
			||||||
    final boolean isOpen = changeDetail.getChange().getStatus().isOpen();
 | 
					    final boolean isOpen = changeDetail.getChange().getStatus().isOpen();
 | 
				
			||||||
    Set<ApprovalCategory.Id> allowed = changeDetail.getCurrentActions();
 | 
					 | 
				
			||||||
    if (allowed == null) {
 | 
					 | 
				
			||||||
      allowed = Collections.emptySet();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (isOpen && allowed.contains(ApprovalCategory.SUBMIT)) {
 | 
					    if (isOpen && changeDetail.canSubmit()) {
 | 
				
			||||||
      final Button b =
 | 
					      final Button b =
 | 
				
			||||||
          new Button(Util.M
 | 
					          new Button(Util.M
 | 
				
			||||||
              .submitPatchSet(detail.getPatchSet().getPatchSetId()));
 | 
					              .submitPatchSet(detail.getPatchSet().getPatchSetId()));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,8 +25,11 @@ import com.google.gerrit.client.ui.PatchLink;
 | 
				
			|||||||
import com.google.gerrit.client.ui.SmallHeading;
 | 
					import com.google.gerrit.client.ui.SmallHeading;
 | 
				
			||||||
import com.google.gerrit.common.PageLinks;
 | 
					import com.google.gerrit.common.PageLinks;
 | 
				
			||||||
import com.google.gerrit.common.data.ApprovalType;
 | 
					import com.google.gerrit.common.data.ApprovalType;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.ApprovalTypes;
 | 
				
			||||||
import com.google.gerrit.common.data.ChangeDetail;
 | 
					import com.google.gerrit.common.data.ChangeDetail;
 | 
				
			||||||
import com.google.gerrit.common.data.PatchSetPublishDetail;
 | 
					import com.google.gerrit.common.data.PatchSetPublishDetail;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.Permission;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.PermissionRange;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
					import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
 | 
					import com.google.gerrit.reviewdb.ApprovalCategoryValue;
 | 
				
			||||||
import com.google.gerrit.reviewdb.Change;
 | 
					import com.google.gerrit.reviewdb.Change;
 | 
				
			||||||
@@ -218,16 +221,25 @@ public class PublishCommentScreen extends AccountScreen implements
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private void initApprovals(final PatchSetPublishDetail r, final Panel body) {
 | 
					  private void initApprovals(final PatchSetPublishDetail r, final Panel body) {
 | 
				
			||||||
    for (final ApprovalType ct : Gerrit.getConfig().getApprovalTypes()
 | 
					    ApprovalTypes types = Gerrit.getConfig().getApprovalTypes();
 | 
				
			||||||
        .getApprovalTypes()) {
 | 
					
 | 
				
			||||||
      if (r.isAllowed(ct.getCategory().getId())) {
 | 
					    for (ApprovalType type : types.getApprovalTypes()) {
 | 
				
			||||||
        initApprovalType(r, body, ct);
 | 
					      String permission = Permission.forLabel(type.getCategory().getLabelName());
 | 
				
			||||||
 | 
					      PermissionRange range = r.getRange(permission);
 | 
				
			||||||
 | 
					      if (range != null && !range.isEmpty()) {
 | 
				
			||||||
 | 
					        initApprovalType(r, body, type, range);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (PermissionRange range : r.getLabels()) {
 | 
				
			||||||
 | 
					      if (!range.isEmpty() && types.byLabel(range.getLabel()) == null) {
 | 
				
			||||||
 | 
					        // TODO: this is a non-standard label. Offer it without the type.
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private void initApprovalType(final PatchSetPublishDetail r,
 | 
					  private void initApprovalType(final PatchSetPublishDetail r,
 | 
				
			||||||
      final Panel body, final ApprovalType ct) {
 | 
					      final Panel body, final ApprovalType ct, final PermissionRange range) {
 | 
				
			||||||
    body.add(new SmallHeading(ct.getCategory().getName() + ":"));
 | 
					    body.add(new SmallHeading(ct.getCategory().getName() + ":"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final VerticalPanel vp = new VerticalPanel();
 | 
					    final VerticalPanel vp = new VerticalPanel();
 | 
				
			||||||
@@ -236,11 +248,10 @@ public class PublishCommentScreen extends AccountScreen implements
 | 
				
			|||||||
        new ArrayList<ApprovalCategoryValue>(ct.getValues());
 | 
					        new ArrayList<ApprovalCategoryValue>(ct.getValues());
 | 
				
			||||||
    Collections.reverse(lst);
 | 
					    Collections.reverse(lst);
 | 
				
			||||||
    final ApprovalCategory.Id catId = ct.getCategory().getId();
 | 
					    final ApprovalCategory.Id catId = ct.getCategory().getId();
 | 
				
			||||||
    final Set<ApprovalCategoryValue.Id> allowed = r.getAllowed(catId);
 | 
					 | 
				
			||||||
    final PatchSetApproval prior = r.getChangeApproval(catId);
 | 
					    final PatchSetApproval prior = r.getChangeApproval(catId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (final ApprovalCategoryValue buttonValue : lst) {
 | 
					    for (final ApprovalCategoryValue buttonValue : lst) {
 | 
				
			||||||
      if (!allowed.contains(buttonValue.getId())) {
 | 
					      if (!range.contains(buttonValue.getValue())) {
 | 
				
			||||||
        continue;
 | 
					        continue;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -306,7 +317,7 @@ public class PublishCommentScreen extends AccountScreen implements
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    submit.setVisible(r.isSubmitAllowed());
 | 
					    submit.setVisible(r.canSubmit());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private void onSend(final boolean submit) {
 | 
					  private void onSend(final boolean submit) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -33,6 +33,7 @@ import com.google.gerrit.reviewdb.ReviewDb;
 | 
				
			|||||||
import com.google.gerrit.server.IdentifiedUser;
 | 
					import com.google.gerrit.server.IdentifiedUser;
 | 
				
			||||||
import com.google.gerrit.server.account.AccountInfoCacheFactory;
 | 
					import com.google.gerrit.server.account.AccountInfoCacheFactory;
 | 
				
			||||||
import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
 | 
					import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.project.CanSubmitResult;
 | 
				
			||||||
import com.google.gerrit.server.project.ChangeControl;
 | 
					import com.google.gerrit.server.project.ChangeControl;
 | 
				
			||||||
import com.google.gerrit.server.project.NoSuchChangeException;
 | 
					import com.google.gerrit.server.project.NoSuchChangeException;
 | 
				
			||||||
import com.google.gerrit.server.workflow.CategoryFunction;
 | 
					import com.google.gerrit.server.workflow.CategoryFunction;
 | 
				
			||||||
@@ -94,6 +95,7 @@ public class ChangeDetailFactory extends Handler<ChangeDetail> {
 | 
				
			|||||||
    if (patch == null) {
 | 
					    if (patch == null) {
 | 
				
			||||||
      throw new NoSuchEntityException();
 | 
					      throw new NoSuchEntityException();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    final CanSubmitResult canSubmitResult = control.canSubmit(patch.getId());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    aic.want(change.getOwner());
 | 
					    aic.want(change.getOwner());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -103,6 +105,7 @@ public class ChangeDetailFactory extends Handler<ChangeDetail> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    detail.setCanAbandon(change.getStatus().isOpen() && control.canAbandon());
 | 
					    detail.setCanAbandon(change.getStatus().isOpen() && control.canAbandon());
 | 
				
			||||||
    detail.setCanRestore(change.getStatus() == Change.Status.ABANDONED && control.canRestore());
 | 
					    detail.setCanRestore(change.getStatus() == Change.Status.ABANDONED && control.canRestore());
 | 
				
			||||||
 | 
					    detail.setCanSubmit(canSubmitResult == CanSubmitResult.OK);
 | 
				
			||||||
    detail.setStarred(control.getCurrentUser().getStarredChanges().contains(
 | 
					    detail.setStarred(control.getCurrentUser().getStarredChanges().contains(
 | 
				
			||||||
        changeId));
 | 
					        changeId));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -141,23 +144,13 @@ public class ChangeDetailFactory extends Handler<ChangeDetail> {
 | 
				
			|||||||
      final Set<ApprovalCategory.Id> missingApprovals =
 | 
					      final Set<ApprovalCategory.Id> missingApprovals =
 | 
				
			||||||
          new HashSet<ApprovalCategory.Id>();
 | 
					          new HashSet<ApprovalCategory.Id>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      final Set<ApprovalCategory.Id> currentActions =
 | 
					 | 
				
			||||||
          new HashSet<ApprovalCategory.Id>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      for (final ApprovalType at : approvalTypes.getApprovalTypes()) {
 | 
					      for (final ApprovalType at : approvalTypes.getApprovalTypes()) {
 | 
				
			||||||
        CategoryFunction.forCategory(at.getCategory()).run(at, fs);
 | 
					        CategoryFunction.forCategory(at.getCategory()).run(at, fs);
 | 
				
			||||||
        if (!fs.isValid(at)) {
 | 
					        if (!fs.isValid(at)) {
 | 
				
			||||||
          missingApprovals.add(at.getCategory().getId());
 | 
					          missingApprovals.add(at.getCategory().getId());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      for (final ApprovalType at : approvalTypes.getActionTypes()) {
 | 
					 | 
				
			||||||
        if (CategoryFunction.forCategory(at.getCategory()).isValid(
 | 
					 | 
				
			||||||
            control.getCurrentUser(), at, fs)) {
 | 
					 | 
				
			||||||
          currentActions.add(at.getCategory().getId());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      detail.setMissingApprovals(missingApprovals);
 | 
					      detail.setMissingApprovals(missingApprovals);
 | 
				
			||||||
      detail.setCurrentActions(currentActions);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final boolean canRemoveReviewers = detail.getChange().getStatus().isOpen() //
 | 
					    final boolean canRemoveReviewers = detail.getChange().getStatus().isOpen() //
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,19 +15,14 @@
 | 
				
			|||||||
package com.google.gerrit.httpd.rpc.changedetail;
 | 
					package com.google.gerrit.httpd.rpc.changedetail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.gerrit.common.data.AccountInfoCache;
 | 
					import com.google.gerrit.common.data.AccountInfoCache;
 | 
				
			||||||
import com.google.gerrit.common.data.ApprovalType;
 | 
					 | 
				
			||||||
import com.google.gerrit.common.data.ApprovalTypes;
 | 
					 | 
				
			||||||
import com.google.gerrit.common.data.PatchSetPublishDetail;
 | 
					import com.google.gerrit.common.data.PatchSetPublishDetail;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.PermissionRange;
 | 
				
			||||||
import com.google.gerrit.httpd.rpc.Handler;
 | 
					import com.google.gerrit.httpd.rpc.Handler;
 | 
				
			||||||
import com.google.gerrit.reviewdb.AccountGroup;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.Change;
 | 
					import com.google.gerrit.reviewdb.Change;
 | 
				
			||||||
import com.google.gerrit.reviewdb.PatchLineComment;
 | 
					import com.google.gerrit.reviewdb.PatchLineComment;
 | 
				
			||||||
import com.google.gerrit.reviewdb.PatchSet;
 | 
					import com.google.gerrit.reviewdb.PatchSet;
 | 
				
			||||||
import com.google.gerrit.reviewdb.PatchSetApproval;
 | 
					import com.google.gerrit.reviewdb.PatchSetApproval;
 | 
				
			||||||
import com.google.gerrit.reviewdb.PatchSetInfo;
 | 
					import com.google.gerrit.reviewdb.PatchSetInfo;
 | 
				
			||||||
import com.google.gerrit.reviewdb.RefRight;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.ReviewDb;
 | 
					import com.google.gerrit.reviewdb.ReviewDb;
 | 
				
			||||||
import com.google.gerrit.server.IdentifiedUser;
 | 
					import com.google.gerrit.server.IdentifiedUser;
 | 
				
			||||||
import com.google.gerrit.server.account.AccountInfoCacheFactory;
 | 
					import com.google.gerrit.server.account.AccountInfoCacheFactory;
 | 
				
			||||||
@@ -36,27 +31,20 @@ import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException;
 | 
				
			|||||||
import com.google.gerrit.server.project.CanSubmitResult;
 | 
					import com.google.gerrit.server.project.CanSubmitResult;
 | 
				
			||||||
import com.google.gerrit.server.project.ChangeControl;
 | 
					import com.google.gerrit.server.project.ChangeControl;
 | 
				
			||||||
import com.google.gerrit.server.project.NoSuchChangeException;
 | 
					import com.google.gerrit.server.project.NoSuchChangeException;
 | 
				
			||||||
import com.google.gerrit.server.project.ProjectCache;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.project.ProjectState;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.project.RefControl;
 | 
					 | 
				
			||||||
import com.google.gwtorm.client.OrmException;
 | 
					import com.google.gwtorm.client.OrmException;
 | 
				
			||||||
import com.google.inject.Inject;
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
import com.google.inject.assistedinject.Assisted;
 | 
					import com.google.inject.assistedinject.Assisted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
import java.util.HashSet;
 | 
					import java.util.Collections;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
import java.util.Set;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
final class PatchSetPublishDetailFactory extends Handler<PatchSetPublishDetail> {
 | 
					final class PatchSetPublishDetailFactory extends Handler<PatchSetPublishDetail> {
 | 
				
			||||||
  interface Factory {
 | 
					  interface Factory {
 | 
				
			||||||
    PatchSetPublishDetailFactory create(PatchSet.Id patchSetId);
 | 
					    PatchSetPublishDetailFactory create(PatchSet.Id patchSetId);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private final ProjectCache projectCache;
 | 
					 | 
				
			||||||
  private final PatchSetInfoFactory infoFactory;
 | 
					  private final PatchSetInfoFactory infoFactory;
 | 
				
			||||||
  private final ApprovalTypes approvalTypes;
 | 
					 | 
				
			||||||
  private final ReviewDb db;
 | 
					  private final ReviewDb db;
 | 
				
			||||||
  private final ChangeControl.Factory changeControlFactory;
 | 
					  private final ChangeControl.Factory changeControlFactory;
 | 
				
			||||||
  private final AccountInfoCacheFactory aic;
 | 
					  private final AccountInfoCacheFactory aic;
 | 
				
			||||||
@@ -68,19 +56,14 @@ final class PatchSetPublishDetailFactory extends Handler<PatchSetPublishDetail>
 | 
				
			|||||||
  private PatchSetInfo patchSetInfo;
 | 
					  private PatchSetInfo patchSetInfo;
 | 
				
			||||||
  private Change change;
 | 
					  private Change change;
 | 
				
			||||||
  private List<PatchLineComment> drafts;
 | 
					  private List<PatchLineComment> drafts;
 | 
				
			||||||
  private Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> allowed;
 | 
					 | 
				
			||||||
  private Map<ApprovalCategory.Id, PatchSetApproval> given;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  PatchSetPublishDetailFactory(final PatchSetInfoFactory infoFactory,
 | 
					  PatchSetPublishDetailFactory(final PatchSetInfoFactory infoFactory,
 | 
				
			||||||
      final ProjectCache projectCache, final ApprovalTypes approvalTypes,
 | 
					 | 
				
			||||||
      final ReviewDb db,
 | 
					      final ReviewDb db,
 | 
				
			||||||
      final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
 | 
					      final AccountInfoCacheFactory.Factory accountInfoCacheFactory,
 | 
				
			||||||
      final ChangeControl.Factory changeControlFactory,
 | 
					      final ChangeControl.Factory changeControlFactory,
 | 
				
			||||||
      final IdentifiedUser user, @Assisted final PatchSet.Id patchSetId) {
 | 
					      final IdentifiedUser user, @Assisted final PatchSet.Id patchSetId) {
 | 
				
			||||||
    this.projectCache = projectCache;
 | 
					 | 
				
			||||||
    this.infoFactory = infoFactory;
 | 
					    this.infoFactory = infoFactory;
 | 
				
			||||||
    this.approvalTypes = approvalTypes;
 | 
					 | 
				
			||||||
    this.db = db;
 | 
					    this.db = db;
 | 
				
			||||||
    this.changeControlFactory = changeControlFactory;
 | 
					    this.changeControlFactory = changeControlFactory;
 | 
				
			||||||
    this.aic = accountInfoCacheFactory.create();
 | 
					    this.aic = accountInfoCacheFactory.create();
 | 
				
			||||||
@@ -98,15 +81,17 @@ final class PatchSetPublishDetailFactory extends Handler<PatchSetPublishDetail>
 | 
				
			|||||||
    patchSetInfo = infoFactory.get(patchSetId);
 | 
					    patchSetInfo = infoFactory.get(patchSetId);
 | 
				
			||||||
    drafts = db.patchComments().draft(patchSetId, user.getAccountId()).toList();
 | 
					    drafts = db.patchComments().draft(patchSetId, user.getAccountId()).toList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    allowed = new HashMap<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>>();
 | 
					    List<PermissionRange> allowed = Collections.emptyList();
 | 
				
			||||||
    given = new HashMap<ApprovalCategory.Id, PatchSetApproval>();
 | 
					    List<PatchSetApproval> given = Collections.emptyList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (change.getStatus().isOpen()
 | 
					    if (change.getStatus().isOpen()
 | 
				
			||||||
        && patchSetId.equals(change.currentPatchSetId())) {
 | 
					        && patchSetId.equals(change.currentPatchSetId())) {
 | 
				
			||||||
      computeAllowed();
 | 
					      allowed = new ArrayList<PermissionRange>(control.getLabelRanges());
 | 
				
			||||||
      for (final PatchSetApproval a : db.patchSetApprovals().byPatchSetUser(
 | 
					      Collections.sort(allowed);
 | 
				
			||||||
          patchSetId, user.getAccountId())) {
 | 
					
 | 
				
			||||||
        given.put(a.getCategoryId(), a);
 | 
					      given = db.patchSetApprovals() //
 | 
				
			||||||
      }
 | 
					          .byPatchSetUser(patchSetId, user.getAccountId()) //
 | 
				
			||||||
 | 
					          .toList();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    aic.want(change.getOwner());
 | 
					    aic.want(change.getOwner());
 | 
				
			||||||
@@ -117,46 +102,12 @@ final class PatchSetPublishDetailFactory extends Handler<PatchSetPublishDetail>
 | 
				
			|||||||
    detail.setPatchSetInfo(patchSetInfo);
 | 
					    detail.setPatchSetInfo(patchSetInfo);
 | 
				
			||||||
    detail.setChange(change);
 | 
					    detail.setChange(change);
 | 
				
			||||||
    detail.setDrafts(drafts);
 | 
					    detail.setDrafts(drafts);
 | 
				
			||||||
    detail.setAllowed(allowed);
 | 
					    detail.setLabels(allowed);
 | 
				
			||||||
    detail.setGiven(given);
 | 
					    detail.setGiven(given);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final CanSubmitResult canSubmitResult = control.canSubmit(patchSetId);
 | 
					    final CanSubmitResult canSubmitResult = control.canSubmit(patchSetId);
 | 
				
			||||||
    detail.setSubmitAllowed(canSubmitResult == CanSubmitResult.OK);
 | 
					    detail.setCanSubmit(canSubmitResult == CanSubmitResult.OK);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return detail;
 | 
					    return detail;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  private void computeAllowed() {
 | 
					 | 
				
			||||||
    final Set<AccountGroup.UUID> am = user.getEffectiveGroups();
 | 
					 | 
				
			||||||
    final ProjectState pe = projectCache.get(change.getProject());
 | 
					 | 
				
			||||||
    for (ApprovalCategory.Id category : approvalTypes.getApprovalCategories()) {
 | 
					 | 
				
			||||||
      RefControl rc = pe.controlFor(user).controlForRef(change.getDest());
 | 
					 | 
				
			||||||
      List<RefRight> categoryRights = rc.getApplicableRights(category);
 | 
					 | 
				
			||||||
      computeAllowed(am, categoryRights, category);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private void computeAllowed(final Set<AccountGroup.UUID> am,
 | 
					 | 
				
			||||||
      final List<RefRight> list, ApprovalCategory.Id category) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Set<ApprovalCategoryValue.Id> s = allowed.get(category);
 | 
					 | 
				
			||||||
    if (s == null) {
 | 
					 | 
				
			||||||
      s = new HashSet<ApprovalCategoryValue.Id>();
 | 
					 | 
				
			||||||
      allowed.put(category, s);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (final RefRight r : list) {
 | 
					 | 
				
			||||||
      if (!am.contains(r.getAccountGroupUUID())) {
 | 
					 | 
				
			||||||
        continue;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      final ApprovalType at =
 | 
					 | 
				
			||||||
          approvalTypes.getApprovalType(r.getApprovalCategoryId());
 | 
					 | 
				
			||||||
      for (short m = r.getMinValue(); m <= r.getMaxValue(); m++) {
 | 
					 | 
				
			||||||
        final ApprovalCategoryValue v = at.getValue(m);
 | 
					 | 
				
			||||||
        if (v != null) {
 | 
					 | 
				
			||||||
          s.add(v.getId());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -188,7 +188,7 @@ class PatchDetailServiceImpl extends BaseServiceImplementation implements
 | 
				
			|||||||
                .byPatchSetUser(ps_id, aid)) {
 | 
					                .byPatchSetUser(ps_id, aid)) {
 | 
				
			||||||
              final ApprovalCategory.Id category = ca.getCategoryId();
 | 
					              final ApprovalCategory.Id category = ca.getCategoryId();
 | 
				
			||||||
              if (change.getStatus().isOpen()) {
 | 
					              if (change.getStatus().isOpen()) {
 | 
				
			||||||
                fs.normalize(approvalTypes.getApprovalType(category), ca);
 | 
					                fs.normalize(approvalTypes.byId(category), ca);
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
              if (ca.getValue() == 0
 | 
					              if (ca.getValue() == 0
 | 
				
			||||||
                  || ApprovalCategory.SUBMIT.equals(category)) {
 | 
					                  || ApprovalCategory.SUBMIT.equals(category)) {
 | 
				
			||||||
@@ -232,7 +232,7 @@ class PatchDetailServiceImpl extends BaseServiceImplementation implements
 | 
				
			|||||||
            for (PatchSetApproval ca : db.patchSetApprovals().byPatchSet(ps_id)) {
 | 
					            for (PatchSetApproval ca : db.patchSetApprovals().byPatchSet(ps_id)) {
 | 
				
			||||||
              final ApprovalCategory.Id category = ca.getCategoryId();
 | 
					              final ApprovalCategory.Id category = ca.getCategoryId();
 | 
				
			||||||
              if (change.getStatus().isOpen()) {
 | 
					              if (change.getStatus().isOpen()) {
 | 
				
			||||||
                fs.normalize(approvalTypes.getApprovalType(category), ca);
 | 
					                fs.normalize(approvalTypes.byId(category), ca);
 | 
				
			||||||
              }
 | 
					              }
 | 
				
			||||||
              if (ca.getValue() == 0
 | 
					              if (ca.getValue() == 0
 | 
				
			||||||
                  || ApprovalCategory.SUBMIT.equals(category)) {
 | 
					                  || ApprovalCategory.SUBMIT.equals(category)) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,33 +24,6 @@ public final class ApprovalCategory {
 | 
				
			|||||||
  public static final ApprovalCategory.Id SUBMIT =
 | 
					  public static final ApprovalCategory.Id SUBMIT =
 | 
				
			||||||
      new ApprovalCategory.Id("SUBM");
 | 
					      new ApprovalCategory.Id("SUBM");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Id of the special "Read" action (and category). */
 | 
					 | 
				
			||||||
  public static final ApprovalCategory.Id READ =
 | 
					 | 
				
			||||||
      new ApprovalCategory.Id("READ");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** Id of the special "Own" category; manages a project. */
 | 
					 | 
				
			||||||
  public static final ApprovalCategory.Id OWN = new ApprovalCategory.Id("OWN");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** Id of the special "Push Annotated Tag" action (and category). */
 | 
					 | 
				
			||||||
  public static final ApprovalCategory.Id PUSH_TAG =
 | 
					 | 
				
			||||||
      new ApprovalCategory.Id("pTAG");
 | 
					 | 
				
			||||||
  public static final short PUSH_TAG_SIGNED = 1;
 | 
					 | 
				
			||||||
  public static final short PUSH_TAG_ANNOTATED = 2;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** Id of the special "Push Branch" action (and category). */
 | 
					 | 
				
			||||||
  public static final ApprovalCategory.Id PUSH_HEAD =
 | 
					 | 
				
			||||||
      new ApprovalCategory.Id("pHD");
 | 
					 | 
				
			||||||
  public static final short PUSH_HEAD_UPDATE = 1;
 | 
					 | 
				
			||||||
  public static final short PUSH_HEAD_CREATE = 2;
 | 
					 | 
				
			||||||
  public static final short PUSH_HEAD_REPLACE = 3;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** Id of the special "Forge Identity" category. */
 | 
					 | 
				
			||||||
  public static final ApprovalCategory.Id FORGE_IDENTITY =
 | 
					 | 
				
			||||||
      new ApprovalCategory.Id("FORG");
 | 
					 | 
				
			||||||
  public static final short FORGE_AUTHOR = 1;
 | 
					 | 
				
			||||||
  public static final short FORGE_COMMITTER = 2;
 | 
					 | 
				
			||||||
  public static final short FORGE_SERVER = 3;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public static class Id extends StringKey<Key<?>> {
 | 
					  public static class Id extends StringKey<Key<?>> {
 | 
				
			||||||
    private static final long serialVersionUID = 1L;
 | 
					    private static final long serialVersionUID = 1L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -73,15 +46,6 @@ public final class ApprovalCategory {
 | 
				
			|||||||
    protected void set(String newValue) {
 | 
					    protected void set(String newValue) {
 | 
				
			||||||
      id = newValue;
 | 
					      id = newValue;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    /** True if the right can be assigned on the wild project. */
 | 
					 | 
				
			||||||
    public boolean canBeOnWildProject() {
 | 
					 | 
				
			||||||
      if (OWN.equals(this)) {
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Internal short unique identifier for this category. */
 | 
					  /** Internal short unique identifier for this category. */
 | 
				
			||||||
@@ -96,16 +60,7 @@ public final class ApprovalCategory {
 | 
				
			|||||||
  @Column(id = 3, length = 4, notNull = false)
 | 
					  @Column(id = 3, length = 4, notNull = false)
 | 
				
			||||||
  protected String abbreviatedName;
 | 
					  protected String abbreviatedName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /** Order of this category within the Approvals table when presented. */
 | 
				
			||||||
   * Order of this category within the Approvals table when presented.
 | 
					 | 
				
			||||||
   * <p>
 | 
					 | 
				
			||||||
   * If < 0 (e.g. -1) this category is not shown in the Approvals table but is
 | 
					 | 
				
			||||||
   * instead considered to be an action that the user might be able to perform,
 | 
					 | 
				
			||||||
   * e.g. "Submit".
 | 
					 | 
				
			||||||
   * <p>
 | 
					 | 
				
			||||||
   * If >= 0 this category is shown in the Approvals table, sorted along with
 | 
					 | 
				
			||||||
   * its siblings by <code>position, name</code>.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  @Column(id = 4)
 | 
					  @Column(id = 4)
 | 
				
			||||||
  protected short position;
 | 
					  protected short position;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -117,6 +72,9 @@ public final class ApprovalCategory {
 | 
				
			|||||||
  @Column(id = 6)
 | 
					  @Column(id = 6)
 | 
				
			||||||
  protected boolean copyMinScore;
 | 
					  protected boolean copyMinScore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Computed name derived from {@link #name}. */
 | 
				
			||||||
 | 
					  protected String labelName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  protected ApprovalCategory() {
 | 
					  protected ApprovalCategory() {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -136,6 +94,26 @@ public final class ApprovalCategory {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  public void setName(final String n) {
 | 
					  public void setName(final String n) {
 | 
				
			||||||
    name = n;
 | 
					    name = n;
 | 
				
			||||||
 | 
					    labelName = null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Clean version of {@link #getName()}, e.g. "Code Review" is "Code-Review". */
 | 
				
			||||||
 | 
					  public String getLabelName() {
 | 
				
			||||||
 | 
					    if (labelName == null) {
 | 
				
			||||||
 | 
					      StringBuilder r = new StringBuilder();
 | 
				
			||||||
 | 
					      for (int i = 0; i < name.length(); i++) {
 | 
				
			||||||
 | 
					        char c = name.charAt(i);
 | 
				
			||||||
 | 
					        if (('0' <= c && c <= '9') //
 | 
				
			||||||
 | 
					            || ('a' <= c && c <= 'z') //
 | 
				
			||||||
 | 
					            || ('A' <= c && c <= 'Z')) {
 | 
				
			||||||
 | 
					          r.append(c);
 | 
				
			||||||
 | 
					        } else if (c == ' ') {
 | 
				
			||||||
 | 
					          r.append('-');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      labelName = r.toString();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return labelName;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public String getAbbreviatedName() {
 | 
					  public String getAbbreviatedName() {
 | 
				
			||||||
@@ -154,14 +132,6 @@ public final class ApprovalCategory {
 | 
				
			|||||||
    position = p;
 | 
					    position = p;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public boolean isAction() {
 | 
					 | 
				
			||||||
    return position < 0;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public boolean isRange() {
 | 
					 | 
				
			||||||
    return !isAction();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public String getFunctionName() {
 | 
					  public String getFunctionName() {
 | 
				
			||||||
    return functionName;
 | 
					    return functionName;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,246 +0,0 @@
 | 
				
			|||||||
// Copyright (C) 2010 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.reviewdb;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.gwtorm.client.Column;
 | 
					 | 
				
			||||||
import com.google.gwtorm.client.CompoundKey;
 | 
					 | 
				
			||||||
import com.google.gwtorm.client.StringKey;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import java.util.Comparator;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** Grant to use an {@link ApprovalCategory} in the scope of a git ref. */
 | 
					 | 
				
			||||||
public final class RefRight {
 | 
					 | 
				
			||||||
  /** Pattern that matches all references in a project. */
 | 
					 | 
				
			||||||
  public static final String ALL = "refs/*";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** Prefix that triggers a regular expression pattern. */
 | 
					 | 
				
			||||||
  public static final String REGEX_PREFIX = "^";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public static class RefPattern extends
 | 
					 | 
				
			||||||
      StringKey<com.google.gwtorm.client.Key<?>> {
 | 
					 | 
				
			||||||
    private static final long serialVersionUID = 1L;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Column(id = 1)
 | 
					 | 
				
			||||||
    protected String pattern;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected RefPattern() {
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public RefPattern(final String pattern) {
 | 
					 | 
				
			||||||
      this.pattern = pattern;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public String get() {
 | 
					 | 
				
			||||||
      return pattern;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void set(String pattern) {
 | 
					 | 
				
			||||||
      this.pattern = pattern;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public static class Key extends CompoundKey<Project.NameKey> {
 | 
					 | 
				
			||||||
    private static final long serialVersionUID = 1L;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Column(id = 1)
 | 
					 | 
				
			||||||
    protected Project.NameKey projectName;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Column(id = 2)
 | 
					 | 
				
			||||||
    protected RefPattern refPattern;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Column(id = 3)
 | 
					 | 
				
			||||||
    protected ApprovalCategory.Id categoryId;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Column(id = 4)
 | 
					 | 
				
			||||||
    protected AccountGroup.Id groupId;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    protected Key() {
 | 
					 | 
				
			||||||
      projectName = new Project.NameKey();
 | 
					 | 
				
			||||||
      refPattern = new RefPattern();
 | 
					 | 
				
			||||||
      categoryId = new ApprovalCategory.Id();
 | 
					 | 
				
			||||||
      groupId = new AccountGroup.Id();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public Key(final Project.NameKey projectName, final RefPattern refPattern,
 | 
					 | 
				
			||||||
        final ApprovalCategory.Id categoryId, final AccountGroup.Id groupId) {
 | 
					 | 
				
			||||||
      this.projectName = projectName;
 | 
					 | 
				
			||||||
      this.refPattern = refPattern;
 | 
					 | 
				
			||||||
      this.categoryId = categoryId;
 | 
					 | 
				
			||||||
      this.groupId = groupId;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public Project.NameKey getParentKey() {
 | 
					 | 
				
			||||||
      return projectName;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public Project.NameKey getProjectNameKey() {
 | 
					 | 
				
			||||||
      return projectName;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public String getRefPattern() {
 | 
					 | 
				
			||||||
      return refPattern.get();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public void setGroupId(AccountGroup.Id groupId) {
 | 
					 | 
				
			||||||
      this.groupId = groupId;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public com.google.gwtorm.client.Key<?>[] members() {
 | 
					 | 
				
			||||||
      return new com.google.gwtorm.client.Key<?>[] {refPattern, categoryId,
 | 
					 | 
				
			||||||
          groupId};
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Column(id = 1, name = Column.NONE)
 | 
					 | 
				
			||||||
  protected Key key;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Column(id = 2)
 | 
					 | 
				
			||||||
  protected short minValue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Column(id = 3)
 | 
					 | 
				
			||||||
  protected short maxValue;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  protected transient AccountGroup.UUID groupUUID;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  protected RefRight() {
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public RefRight(RefRight.Key key) {
 | 
					 | 
				
			||||||
    this.key = key;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public RefRight(final RefRight refRight, final AccountGroup.UUID groupId) {
 | 
					 | 
				
			||||||
    this(new RefRight.Key(refRight.getKey().projectName,
 | 
					 | 
				
			||||||
        refRight.getKey().refPattern, refRight.getKey().categoryId, null));
 | 
					 | 
				
			||||||
    setMinValue(refRight.getMinValue());
 | 
					 | 
				
			||||||
    setMaxValue(refRight.getMaxValue());
 | 
					 | 
				
			||||||
    setAccountGroupUUID(groupId);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public RefRight.Key getKey() {
 | 
					 | 
				
			||||||
    return key;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public String getRefPattern() {
 | 
					 | 
				
			||||||
    if (isExclusive()) {
 | 
					 | 
				
			||||||
      return key.refPattern.get().substring(1);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return key.refPattern.get();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public String getRefPatternForDisplay() {
 | 
					 | 
				
			||||||
    return key.refPattern.get();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public Project.NameKey getProjectNameKey() {
 | 
					 | 
				
			||||||
    return getKey().getProjectNameKey();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public boolean isExclusive() {
 | 
					 | 
				
			||||||
    return key.refPattern.get().startsWith("-");
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public ApprovalCategory.Id getApprovalCategoryId() {
 | 
					 | 
				
			||||||
    return key.categoryId;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public AccountGroup.Id getAccountGroupId() {
 | 
					 | 
				
			||||||
    return key.groupId;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public AccountGroup.UUID getAccountGroupUUID() {
 | 
					 | 
				
			||||||
    return groupUUID;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public void setAccountGroupUUID(AccountGroup.UUID uuid) {
 | 
					 | 
				
			||||||
    groupUUID = uuid;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public short getMinValue() {
 | 
					 | 
				
			||||||
    return minValue;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public void setMinValue(final short m) {
 | 
					 | 
				
			||||||
    minValue = m;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public short getMaxValue() {
 | 
					 | 
				
			||||||
    return maxValue;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public void setMaxValue(final short m) {
 | 
					 | 
				
			||||||
    maxValue = m;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public String toString() {
 | 
					 | 
				
			||||||
    StringBuilder s = new StringBuilder();
 | 
					 | 
				
			||||||
    s.append("{group :");
 | 
					 | 
				
			||||||
    s.append(getAccountGroupId().get());
 | 
					 | 
				
			||||||
    s.append(", proj :");
 | 
					 | 
				
			||||||
    s.append(getProjectNameKey().get());
 | 
					 | 
				
			||||||
    s.append(", cat :");
 | 
					 | 
				
			||||||
    s.append(getApprovalCategoryId().get());
 | 
					 | 
				
			||||||
    s.append(", pattern :");
 | 
					 | 
				
			||||||
    s.append(getRefPatternForDisplay());
 | 
					 | 
				
			||||||
    s.append(", min :");
 | 
					 | 
				
			||||||
    s.append(getMinValue());
 | 
					 | 
				
			||||||
    s.append(", max :");
 | 
					 | 
				
			||||||
    s.append(getMaxValue());
 | 
					 | 
				
			||||||
    s.append("}");
 | 
					 | 
				
			||||||
    return s.toString();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public int hashCode() {
 | 
					 | 
				
			||||||
    return getKey().hashCode();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public boolean equals(Object o) {
 | 
					 | 
				
			||||||
    if (o instanceof RefRight) {
 | 
					 | 
				
			||||||
      RefRight a = this;
 | 
					 | 
				
			||||||
      RefRight b = (RefRight) o;
 | 
					 | 
				
			||||||
      return a.getKey().equals(b.getKey())
 | 
					 | 
				
			||||||
          && a.getMinValue() == b.getMinValue()
 | 
					 | 
				
			||||||
          && a.getMaxValue() == b.getMaxValue();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public static final Comparator<RefRight> REF_PATTERN_ORDER =
 | 
					 | 
				
			||||||
      new Comparator<RefRight>() {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public int compare(RefRight a, RefRight b) {
 | 
					 | 
				
			||||||
      int aLength = a.getRefPattern().length();
 | 
					 | 
				
			||||||
      int bLength = b.getRefPattern().length();
 | 
					 | 
				
			||||||
      if (bLength == aLength) {
 | 
					 | 
				
			||||||
        ApprovalCategory.Id aCat = a.getApprovalCategoryId();
 | 
					 | 
				
			||||||
        ApprovalCategory.Id bCat = b.getApprovalCategoryId();
 | 
					 | 
				
			||||||
        if (aCat.get().equals(bCat.get())) {
 | 
					 | 
				
			||||||
          return a.getRefPattern().compareTo(b.getRefPattern());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return a.getApprovalCategoryId().get()
 | 
					 | 
				
			||||||
            .compareTo(b.getApprovalCategoryId().get());
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      return bLength - aLength;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,33 +0,0 @@
 | 
				
			|||||||
// Copyright (C) 2010 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.reviewdb;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.gwtorm.client.Access;
 | 
					 | 
				
			||||||
import com.google.gwtorm.client.OrmException;
 | 
					 | 
				
			||||||
import com.google.gwtorm.client.PrimaryKey;
 | 
					 | 
				
			||||||
import com.google.gwtorm.client.Query;
 | 
					 | 
				
			||||||
import com.google.gwtorm.client.ResultSet;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public interface RefRightAccess extends Access<RefRight, RefRight.Key> {
 | 
					 | 
				
			||||||
  @PrimaryKey("key")
 | 
					 | 
				
			||||||
  RefRight get(RefRight.Key refRight) throws OrmException;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Query("WHERE key.projectName = ?")
 | 
					 | 
				
			||||||
  ResultSet<RefRight> byProject(Project.NameKey project) throws OrmException;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Query("WHERE key.categoryId = ? AND key.groupId = ?")
 | 
					 | 
				
			||||||
  ResultSet<RefRight> byCategoryGroup(ApprovalCategory.Id cat,
 | 
					 | 
				
			||||||
      AccountGroup.Id group) throws OrmException;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -110,9 +110,6 @@ public interface ReviewDb extends Schema {
 | 
				
			|||||||
  @Relation
 | 
					  @Relation
 | 
				
			||||||
  PatchLineCommentAccess patchComments();
 | 
					  PatchLineCommentAccess patchComments();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Relation
 | 
					 | 
				
			||||||
  RefRightAccess refRights();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Relation
 | 
					  @Relation
 | 
				
			||||||
  TrackingIdAccess trackingIds();
 | 
					  TrackingIdAccess trackingIds();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -157,14 +157,6 @@ ON patch_set_ancestors (ancestor_revision);
 | 
				
			|||||||
--    @PrimaryKey covers: all, suggestByName
 | 
					--    @PrimaryKey covers: all, suggestByName
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- *********************************************************************
 | 
					 | 
				
			||||||
-- RefRightAccess
 | 
					 | 
				
			||||||
--    @PrimaryKey covers: byProject
 | 
					 | 
				
			||||||
--    covers:             byCategoryGroup
 | 
					 | 
				
			||||||
CREATE INDEX ref_rights_byCatGroup
 | 
					 | 
				
			||||||
ON ref_rights (category_id, group_id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-- *********************************************************************
 | 
					-- *********************************************************************
 | 
				
			||||||
-- TrackingIdAccess
 | 
					-- TrackingIdAccess
 | 
				
			||||||
--
 | 
					--
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -239,14 +239,6 @@ ON patch_set_ancestors (ancestor_revision);
 | 
				
			|||||||
--    covers:             ownedByGroup
 | 
					--    covers:             ownedByGroup
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-- *********************************************************************
 | 
					 | 
				
			||||||
-- RefRightAccess
 | 
					 | 
				
			||||||
--    @PrimaryKey covers: byProject
 | 
					 | 
				
			||||||
--    covers:             byCategoryGroup
 | 
					 | 
				
			||||||
CREATE INDEX ref_rights_byCatGroup
 | 
					 | 
				
			||||||
ON ref_rights (category_id, group_id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
-- *********************************************************************
 | 
					-- *********************************************************************
 | 
				
			||||||
-- TrackingIdAccess
 | 
					-- TrackingIdAccess
 | 
				
			||||||
--
 | 
					--
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -433,7 +433,7 @@ public class ChangeHookRunner {
 | 
				
			|||||||
            Entry<ApprovalCategory.Id, ApprovalCategoryValue.Id> approval) {
 | 
					            Entry<ApprovalCategory.Id, ApprovalCategoryValue.Id> approval) {
 | 
				
			||||||
        ApprovalAttribute a = new ApprovalAttribute();
 | 
					        ApprovalAttribute a = new ApprovalAttribute();
 | 
				
			||||||
        a.type = approval.getKey().get();
 | 
					        a.type = approval.getKey().get();
 | 
				
			||||||
        final ApprovalType at = approvalTypes.getApprovalType(approval.getKey());
 | 
					        final ApprovalType at = approvalTypes.byId(approval.getKey());
 | 
				
			||||||
        a.description = at.getCategory().getName();
 | 
					        a.description = at.getCategory().getName();
 | 
				
			||||||
        a.value = Short.toString(approval.getValue().get());
 | 
					        a.value = Short.toString(approval.getValue().get());
 | 
				
			||||||
        return a;
 | 
					        return a;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,8 +39,7 @@ class ApprovalTypesProvider implements Provider<ApprovalTypes> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public ApprovalTypes get() {
 | 
					  public ApprovalTypes get() {
 | 
				
			||||||
    List<ApprovalType> approvalTypes = new ArrayList<ApprovalType>(2);
 | 
					    List<ApprovalType> types = new ArrayList<ApprovalType>(2);
 | 
				
			||||||
    List<ApprovalType> actionTypes = new ArrayList<ApprovalType>(2);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      final ReviewDb db = schema.open();
 | 
					      final ReviewDb db = schema.open();
 | 
				
			||||||
@@ -48,12 +47,7 @@ class ApprovalTypesProvider implements Provider<ApprovalTypes> {
 | 
				
			|||||||
        for (final ApprovalCategory c : db.approvalCategories().all()) {
 | 
					        for (final ApprovalCategory c : db.approvalCategories().all()) {
 | 
				
			||||||
          final List<ApprovalCategoryValue> values =
 | 
					          final List<ApprovalCategoryValue> values =
 | 
				
			||||||
              db.approvalCategoryValues().byCategory(c.getId()).toList();
 | 
					              db.approvalCategoryValues().byCategory(c.getId()).toList();
 | 
				
			||||||
          final ApprovalType type = new ApprovalType(c, values);
 | 
					          types.add(new ApprovalType(c, values));
 | 
				
			||||||
          if (type.getCategory().isAction()) {
 | 
					 | 
				
			||||||
            actionTypes.add(type);
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            approvalTypes.add(type);
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } finally {
 | 
					      } finally {
 | 
				
			||||||
        db.close();
 | 
					        db.close();
 | 
				
			||||||
@@ -62,8 +56,6 @@ class ApprovalTypesProvider implements Provider<ApprovalTypes> {
 | 
				
			|||||||
      throw new ProvisionException("Cannot query approval categories", e);
 | 
					      throw new ProvisionException("Cannot query approval categories", e);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    approvalTypes = Collections.unmodifiableList(approvalTypes);
 | 
					    return new ApprovalTypes(Collections.unmodifiableList(types));
 | 
				
			||||||
    actionTypes = Collections.unmodifiableList(actionTypes);
 | 
					 | 
				
			||||||
    return new ApprovalTypes(approvalTypes, actionTypes);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -212,7 +212,7 @@ public class EventFactory {
 | 
				
			|||||||
    a.by = asAccountAttribute(approval.getAccountId());
 | 
					    a.by = asAccountAttribute(approval.getAccountId());
 | 
				
			||||||
    a.grantedOn = approval.getGranted().getTime() / 1000L;
 | 
					    a.grantedOn = approval.getGranted().getTime() / 1000L;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ApprovalType at = approvalTypes.getApprovalType(approval.getCategoryId());
 | 
					    ApprovalType at = approvalTypes.byId(approval.getCategoryId());
 | 
				
			||||||
    if (at != null) {
 | 
					    if (at != null) {
 | 
				
			||||||
      a.description = at.getCategory().getName();
 | 
					      a.description = at.getCategory().getName();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -212,10 +212,13 @@ public class CreateCodeReviewNotes {
 | 
				
			|||||||
        if (ApprovalCategory.SUBMIT.equals(a.getCategoryId())) {
 | 
					        if (ApprovalCategory.SUBMIT.equals(a.getCategoryId())) {
 | 
				
			||||||
          submit = a;
 | 
					          submit = a;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          formatter.appendApproval(
 | 
					          ApprovalCategory type = approvalTypes.byId(a.getCategoryId()).getCategory();
 | 
				
			||||||
              approvalTypes.getApprovalType(a.getCategoryId()).getCategory(),
 | 
					          if (type != null) {
 | 
				
			||||||
              a.getValue(),
 | 
					            formatter.appendApproval(
 | 
				
			||||||
              accountCache.get(a.getAccountId()).getAccount());
 | 
					                type,
 | 
				
			||||||
 | 
					                a.getValue(),
 | 
				
			||||||
 | 
					                accountCache.get(a.getAccountId()).getAccount());
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -805,7 +805,7 @@ public class MergeOp {
 | 
				
			|||||||
          tag = "Tested-by";
 | 
					          tag = "Tested-by";
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          final ApprovalType at =
 | 
					          final ApprovalType at =
 | 
				
			||||||
              approvalTypes.getApprovalType(a.getCategoryId());
 | 
					              approvalTypes.byId(a.getCategoryId());
 | 
				
			||||||
          if (at == null) {
 | 
					          if (at == null) {
 | 
				
			||||||
            // A deprecated/deleted approval type, ignore it.
 | 
					            // A deprecated/deleted approval type, ignore it.
 | 
				
			||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,24 +14,45 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.google.gerrit.server.git;
 | 
					package com.google.gerrit.server.git;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.google.gerrit.common.data.AccessSection.isAccessSection;
 | 
				
			||||||
 | 
					import static com.google.gerrit.common.data.Permission.isPermission;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.Project;
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
import com.google.gerrit.reviewdb.Project.SubmitType;
 | 
					import com.google.gerrit.reviewdb.Project.SubmitType;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.account.GroupCache;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.eclipse.jgit.errors.ConfigInvalidException;
 | 
					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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.BufferedReader;
 | 
				
			||||||
import java.io.IOException;
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.io.StringReader;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.Collection;
 | 
				
			||||||
 | 
					import java.util.Collections;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.HashSet;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class ProjectConfig extends VersionedMetaData {
 | 
					public class ProjectConfig extends VersionedMetaData {
 | 
				
			||||||
  private static final String PROJECT_CONFIG = "project.config";
 | 
					  private static final String PROJECT_CONFIG = "project.config";
 | 
				
			||||||
 | 
					  private static final String GROUP_LIST = "groups";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static final String PROJECT = "project";
 | 
					  private static final String PROJECT = "project";
 | 
				
			||||||
  private static final String KEY_DESCRIPTION = "description";
 | 
					  private static final String KEY_DESCRIPTION = "description";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static final String ACCESS = "access";
 | 
					  private static final String ACCESS = "access";
 | 
				
			||||||
  private static final String KEY_INHERIT_FROM = "inheritFrom";
 | 
					  private static final String KEY_INHERIT_FROM = "inheritFrom";
 | 
				
			||||||
 | 
					  private static final String KEY_GROUP_PERMISSIONS = "exclusiveGroupPermissions";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static final String RECEIVE = "receive";
 | 
					  private static final String RECEIVE = "receive";
 | 
				
			||||||
  private static final String KEY_REQUIRE_SIGNED_OFF_BY = "requireSignedOffBy";
 | 
					  private static final String KEY_REQUIRE_SIGNED_OFF_BY = "requireSignedOffBy";
 | 
				
			||||||
@@ -48,6 +69,8 @@ public class ProjectConfig extends VersionedMetaData {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  private Project.NameKey projectName;
 | 
					  private Project.NameKey projectName;
 | 
				
			||||||
  private Project project;
 | 
					  private Project project;
 | 
				
			||||||
 | 
					  private Map<AccountGroup.UUID, GroupReference> groupsByUUID;
 | 
				
			||||||
 | 
					  private Map<String, AccessSection> accessSections;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static ProjectConfig read(MetaDataUpdate update) throws IOException,
 | 
					  public static ProjectConfig read(MetaDataUpdate update) throws IOException,
 | 
				
			||||||
      ConfigInvalidException {
 | 
					      ConfigInvalidException {
 | 
				
			||||||
@@ -71,6 +94,60 @@ public class ProjectConfig extends VersionedMetaData {
 | 
				
			|||||||
    return project;
 | 
					    return project;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public AccessSection getAccessSection(String name) {
 | 
				
			||||||
 | 
					    return getAccessSection(name, false);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public AccessSection getAccessSection(String name, boolean create) {
 | 
				
			||||||
 | 
					    AccessSection as = accessSections.get(name);
 | 
				
			||||||
 | 
					    if (as == null && create) {
 | 
				
			||||||
 | 
					      as = new AccessSection(name);
 | 
				
			||||||
 | 
					      accessSections.put(name, as);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return as;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public Collection<AccessSection> getAccessSections() {
 | 
				
			||||||
 | 
					    return accessSections.values();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public void remove(AccessSection section) {
 | 
				
			||||||
 | 
					    accessSections.remove(section.getRefPattern());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public GroupReference resolve(AccountGroup group) {
 | 
				
			||||||
 | 
					    return resolve(GroupReference.forGroup(group));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public GroupReference resolve(GroupReference group) {
 | 
				
			||||||
 | 
					    if (group != null) {
 | 
				
			||||||
 | 
					      GroupReference ref = groupsByUUID.get(group.getUUID());
 | 
				
			||||||
 | 
					      if (ref != null) {
 | 
				
			||||||
 | 
					        return ref;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      groupsByUUID.put(group.getUUID(), group);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return group;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Check all GroupReferences use current group name, repairing stale ones.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * @param groupCache cache to use when looking up group information by UUID.
 | 
				
			||||||
 | 
					   * @return true if one or more group names was stale.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  public boolean updateGroupNames(GroupCache groupCache) {
 | 
				
			||||||
 | 
					    boolean dirty = false;
 | 
				
			||||||
 | 
					    for (GroupReference ref : groupsByUUID.values()) {
 | 
				
			||||||
 | 
					      AccountGroup g = groupCache.get(ref.getUUID());
 | 
				
			||||||
 | 
					      if (g != null && !g.getName().equals(ref.getName())) {
 | 
				
			||||||
 | 
					        dirty = true;
 | 
				
			||||||
 | 
					        ref.setName(g.getName());
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return dirty;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  protected String getRefName() {
 | 
					  protected String getRefName() {
 | 
				
			||||||
    return GitRepositoryManager.REF_CONFIG;
 | 
					    return GitRepositoryManager.REF_CONFIG;
 | 
				
			||||||
@@ -78,6 +155,8 @@ public class ProjectConfig extends VersionedMetaData {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  protected void onLoad() throws IOException, ConfigInvalidException {
 | 
					  protected void onLoad() throws IOException, ConfigInvalidException {
 | 
				
			||||||
 | 
					    Map<String,GroupReference> groupsByName = readGroupList();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Config rc = readConfig(PROJECT_CONFIG);
 | 
					    Config rc = readConfig(PROJECT_CONFIG);
 | 
				
			||||||
    project = new Project(projectName);
 | 
					    project = new Project(projectName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -94,6 +173,80 @@ public class ProjectConfig extends VersionedMetaData {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    p.setSubmitType(rc.getEnum(SUBMIT, null, KEY_ACTION, defaultSubmitAction));
 | 
					    p.setSubmitType(rc.getEnum(SUBMIT, null, KEY_ACTION, defaultSubmitAction));
 | 
				
			||||||
    p.setUseContentMerge(rc.getBoolean(SUBMIT, null, KEY_MERGE_CONTENT, false));
 | 
					    p.setUseContentMerge(rc.getBoolean(SUBMIT, null, KEY_MERGE_CONTENT, false));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    accessSections = new HashMap<String, AccessSection>();
 | 
				
			||||||
 | 
					    for (String refName : rc.getSubsections(ACCESS)) {
 | 
				
			||||||
 | 
					      if (isAccessSection(refName)) {
 | 
				
			||||||
 | 
					        AccessSection as = getAccessSection(refName, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (String varName : rc.getStringList(ACCESS, refName, KEY_GROUP_PERMISSIONS)) {
 | 
				
			||||||
 | 
					          for (String n : varName.split("[, \t]{1,}")) {
 | 
				
			||||||
 | 
					            if (isPermission(n)) {
 | 
				
			||||||
 | 
					              as.getPermission(n, true).setExclusiveGroup(true);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (String varName : rc.getNames(ACCESS, refName)) {
 | 
				
			||||||
 | 
					          if (isPermission(varName)) {
 | 
				
			||||||
 | 
					            Permission perm = as.getPermission(varName, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            boolean useRange = perm.isLabel();
 | 
				
			||||||
 | 
					            for (String ruleString : rc.getStringList(ACCESS, refName, varName)) {
 | 
				
			||||||
 | 
					              PermissionRule rule;
 | 
				
			||||||
 | 
					              try {
 | 
				
			||||||
 | 
					                rule = PermissionRule.fromString(ruleString, useRange);
 | 
				
			||||||
 | 
					              } catch (IllegalArgumentException notRule) {
 | 
				
			||||||
 | 
					                throw new ConfigInvalidException("Invalid rule in " + ACCESS
 | 
				
			||||||
 | 
					                    + "." + refName + "." + varName + ": "
 | 
				
			||||||
 | 
					                    + notRule.getMessage(), notRule);
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              GroupReference ref = groupsByName.get(rule.getGroup().getName());
 | 
				
			||||||
 | 
					              if (ref == null) {
 | 
				
			||||||
 | 
					                // The group wasn't mentioned in the groups table, so there is
 | 
				
			||||||
 | 
					                // no valid UUID for it. Pool the reference anyway so at least
 | 
				
			||||||
 | 
					                // all rules in the same file share the same GroupReference.
 | 
				
			||||||
 | 
					                //
 | 
				
			||||||
 | 
					                ref = rule.getGroup();
 | 
				
			||||||
 | 
					                groupsByName.put(ref.getName(), ref);
 | 
				
			||||||
 | 
					              }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              rule.setGroup(ref);
 | 
				
			||||||
 | 
					              perm.add(rule);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private Map<String, GroupReference> readGroupList() throws IOException,
 | 
				
			||||||
 | 
					      ConfigInvalidException {
 | 
				
			||||||
 | 
					    groupsByUUID = new HashMap<AccountGroup.UUID, GroupReference>();
 | 
				
			||||||
 | 
					    Map<String, GroupReference> groupsByName =
 | 
				
			||||||
 | 
					        new HashMap<String, GroupReference>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    BufferedReader br = new BufferedReader(new StringReader(readUTF8(GROUP_LIST)));
 | 
				
			||||||
 | 
					    String s;
 | 
				
			||||||
 | 
					    while ((s = br.readLine()) != null) {
 | 
				
			||||||
 | 
					      if (s.isEmpty() || s.startsWith("#")) {
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      int tab = s.indexOf('\t');
 | 
				
			||||||
 | 
					      if (tab < 0) {
 | 
				
			||||||
 | 
					        throw new ConfigInvalidException("Invalid group line: " + s);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      AccountGroup.UUID uuid = new AccountGroup.UUID(s.substring(0, tab).trim());
 | 
				
			||||||
 | 
					      String name = s.substring(tab + 1).trim();
 | 
				
			||||||
 | 
					      GroupReference ref = new GroupReference(uuid, name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      groupsByUUID.put(uuid, ref);
 | 
				
			||||||
 | 
					      groupsByName.put(name, ref);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return groupsByName;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
@@ -120,6 +273,102 @@ public class ProjectConfig extends VersionedMetaData {
 | 
				
			|||||||
    set(rc, SUBMIT, null, KEY_ACTION, p.getSubmitType(), defaultSubmitAction);
 | 
					    set(rc, SUBMIT, null, KEY_ACTION, p.getSubmitType(), defaultSubmitAction);
 | 
				
			||||||
    set(rc, SUBMIT, null, KEY_MERGE_CONTENT, p.isUseContentMerge());
 | 
					    set(rc, SUBMIT, null, KEY_MERGE_CONTENT, p.isUseContentMerge());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Set<AccountGroup.UUID> keepGroups = new HashSet<AccountGroup.UUID>();
 | 
				
			||||||
 | 
					    for (AccessSection as : sort(accessSections.values())) {
 | 
				
			||||||
 | 
					      String refName = as.getRefPattern();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      StringBuilder doNotInherit = new StringBuilder();
 | 
				
			||||||
 | 
					      for (Permission perm : sort(as.getPermissions())) {
 | 
				
			||||||
 | 
					        if (perm.getExclusiveGroup()) {
 | 
				
			||||||
 | 
					          if (0 < doNotInherit.length()) {
 | 
				
			||||||
 | 
					            doNotInherit.append(' ');
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          doNotInherit.append(perm.getName());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (0 < doNotInherit.length()) {
 | 
				
			||||||
 | 
					        rc.setString(ACCESS, refName, KEY_GROUP_PERMISSIONS, doNotInherit.toString());
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        rc.unset(ACCESS, refName, KEY_GROUP_PERMISSIONS);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      Set<String> have = new HashSet<String>();
 | 
				
			||||||
 | 
					      for (Permission permission : sort(as.getPermissions())) {
 | 
				
			||||||
 | 
					        have.add(permission.getName().toLowerCase());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        boolean needRange = permission.isLabel();
 | 
				
			||||||
 | 
					        List<String> rules = new ArrayList<String>();
 | 
				
			||||||
 | 
					        for (PermissionRule rule : sort(permission.getRules())) {
 | 
				
			||||||
 | 
					          GroupReference group = rule.getGroup();
 | 
				
			||||||
 | 
					          if (group.getUUID() != null) {
 | 
				
			||||||
 | 
					            keepGroups.add(group.getUUID());
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          rules.add(rule.asString(needRange));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        rc.setStringList(ACCESS, refName, permission.getName(), rules);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (String varName : rc.getNames(ACCESS, refName)) {
 | 
				
			||||||
 | 
					        if (isPermission(varName) && !have.contains(varName.toLowerCase())) {
 | 
				
			||||||
 | 
					          rc.unset(ACCESS, refName, varName);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (String name : rc.getSubsections(ACCESS)) {
 | 
				
			||||||
 | 
					      if (isAccessSection(name) && !accessSections.containsKey(name)) {
 | 
				
			||||||
 | 
					        rc.unsetSection(ACCESS, name);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    groupsByUUID.keySet().retainAll(keepGroups);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    saveConfig(PROJECT_CONFIG, rc);
 | 
					    saveConfig(PROJECT_CONFIG, rc);
 | 
				
			||||||
 | 
					    saveGroupList();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void saveGroupList() throws IOException {
 | 
				
			||||||
 | 
					    if (groupsByUUID.isEmpty()) {
 | 
				
			||||||
 | 
					      saveFile(GROUP_LIST, null);
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    final int uuidLen = 40;
 | 
				
			||||||
 | 
					    StringBuilder buf = new StringBuilder();
 | 
				
			||||||
 | 
					    buf.append(pad(uuidLen, "# UUID"));
 | 
				
			||||||
 | 
					    buf.append('\t');
 | 
				
			||||||
 | 
					    buf.append("Group Name");
 | 
				
			||||||
 | 
					    buf.append('\n');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    buf.append('#');
 | 
				
			||||||
 | 
					    buf.append('\n');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (GroupReference g : sort(groupsByUUID.values())) {
 | 
				
			||||||
 | 
					      if (g.getUUID() != null && g.getName() != null) {
 | 
				
			||||||
 | 
					        buf.append(pad(uuidLen, g.getUUID().get()));
 | 
				
			||||||
 | 
					        buf.append('\t');
 | 
				
			||||||
 | 
					        buf.append(g.getName());
 | 
				
			||||||
 | 
					        buf.append('\n');
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    saveUTF8(GROUP_LIST, buf.toString());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static String pad(int len, String src) {
 | 
				
			||||||
 | 
					    if (len <= src.length()) {
 | 
				
			||||||
 | 
					      return src;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    StringBuilder r = new StringBuilder(len);
 | 
				
			||||||
 | 
					    r.append(src);
 | 
				
			||||||
 | 
					    while (r.length() < len) {
 | 
				
			||||||
 | 
					      r.append(' ');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return r.toString();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static <T extends Comparable<? super T>> List<T> sort(Collection<T> m) {
 | 
				
			||||||
 | 
					    ArrayList<T> r = new ArrayList<T>(m);
 | 
				
			||||||
 | 
					    Collections.sort(r);
 | 
				
			||||||
 | 
					    return r;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1359,7 +1359,7 @@ public class ReceiveCommits implements PreReceiveHook, PostReceiveHook {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      final ApprovalType type =
 | 
					      final ApprovalType type =
 | 
				
			||||||
          approvalTypes.getApprovalType(a.getCategoryId());
 | 
					          approvalTypes.byId(a.getCategoryId());
 | 
				
			||||||
      if (a.getPatchSetId().equals(priorPatchSet)
 | 
					      if (a.getPatchSetId().equals(priorPatchSet)
 | 
				
			||||||
          && type.getCategory().isCopyMinScore() && type.isMaxNegative(a)) {
 | 
					          && type.getCategory().isCopyMinScore() && type.isMaxNegative(a)) {
 | 
				
			||||||
        // If there was a negative vote on the prior patch set, carry it
 | 
					        // If there was a negative vote on the prior patch set, carry it
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -51,8 +51,7 @@ class ReviewNoteHeaderFormatter {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  void appendApproval(ApprovalCategory category,
 | 
					  void appendApproval(ApprovalCategory category,
 | 
				
			||||||
      short value, Account user) {
 | 
					      short value, Account user) {
 | 
				
			||||||
    // TODO: use category.getLabel() when available
 | 
					    sb.append(category.getLabelName());
 | 
				
			||||||
    sb.append(category.getName().replace(' ', '-'));
 | 
					 | 
				
			||||||
    sb.append(value < 0 ? "-" : "+").append(Math.abs(value)).append(": ");
 | 
					    sb.append(value < 0 ? "-" : "+").append(Math.abs(value)).append(": ");
 | 
				
			||||||
    appendUserData(user);
 | 
					    appendUserData(user);
 | 
				
			||||||
    sb.append("\n");
 | 
					    sb.append("\n");
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -69,6 +69,18 @@ public abstract class VersionedMetaData {
 | 
				
			|||||||
    return revision.copy();
 | 
					    return revision.copy();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Initialize in-memory as though the repository branch doesn't exist. */
 | 
				
			||||||
 | 
					  public void createInMemory() {
 | 
				
			||||||
 | 
					    try {
 | 
				
			||||||
 | 
					      revision = null;
 | 
				
			||||||
 | 
					      onLoad();
 | 
				
			||||||
 | 
					    } catch (IOException err) {
 | 
				
			||||||
 | 
					      throw new RuntimeException("Unexpected IOException", err);
 | 
				
			||||||
 | 
					    } catch (ConfigInvalidException err) {
 | 
				
			||||||
 | 
					      throw new RuntimeException("Unexpected ConfigInvalidException", err);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Load the current version from the branch.
 | 
					   * Load the current version from the branch.
 | 
				
			||||||
   * <p>
 | 
					   * <p>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -171,7 +171,7 @@ public class PublishComments implements Callable<VoidResult> {
 | 
				
			|||||||
      final short o = a.getValue();
 | 
					      final short o = a.getValue();
 | 
				
			||||||
      a.setValue(want.get());
 | 
					      a.setValue(want.get());
 | 
				
			||||||
      a.cache(change);
 | 
					      a.cache(change);
 | 
				
			||||||
      functionState.normalize(types.getApprovalType(a.getCategoryId()), a);
 | 
					      functionState.normalize(types.byId(a.getCategoryId()), a);
 | 
				
			||||||
      if (o != a.getValue()) {
 | 
					      if (o != a.getValue()) {
 | 
				
			||||||
        // Value changed, ensure we update the database.
 | 
					        // Value changed, ensure we update the database.
 | 
				
			||||||
        //
 | 
					        //
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,13 +16,12 @@ package com.google.gerrit.server.project;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.google.gerrit.common.data.ApprovalType;
 | 
					import com.google.gerrit.common.data.ApprovalType;
 | 
				
			||||||
import com.google.gerrit.common.data.ApprovalTypes;
 | 
					import com.google.gerrit.common.data.ApprovalTypes;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
					import com.google.gerrit.common.data.PermissionRange;
 | 
				
			||||||
import com.google.gerrit.reviewdb.Change;
 | 
					import com.google.gerrit.reviewdb.Change;
 | 
				
			||||||
import com.google.gerrit.reviewdb.PatchSet;
 | 
					import com.google.gerrit.reviewdb.PatchSet;
 | 
				
			||||||
import com.google.gerrit.reviewdb.PatchSetApproval;
 | 
					import com.google.gerrit.reviewdb.PatchSetApproval;
 | 
				
			||||||
import com.google.gerrit.reviewdb.Project;
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ReviewDb;
 | 
					import com.google.gerrit.reviewdb.ReviewDb;
 | 
				
			||||||
import com.google.gerrit.server.ChangeUtil;
 | 
					 | 
				
			||||||
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.workflow.CategoryFunction;
 | 
					import com.google.gerrit.server.workflow.CategoryFunction;
 | 
				
			||||||
@@ -31,7 +30,6 @@ import com.google.gwtorm.client.OrmException;
 | 
				
			|||||||
import com.google.inject.Inject;
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
import com.google.inject.Provider;
 | 
					import com.google.inject.Provider;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.ArrayList;
 | 
					 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -164,8 +162,14 @@ public class ChangeControl {
 | 
				
			|||||||
    return canAbandon(); // Anyone who can abandon the change can restore it back
 | 
					    return canAbandon(); // Anyone who can abandon the change can restore it back
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public short normalize(ApprovalCategory.Id category, short score) {
 | 
					  /** All value ranges of any allowed label permission. */
 | 
				
			||||||
    return getRefControl().normalize(category, score);
 | 
					  public List<PermissionRange> getLabelRanges() {
 | 
				
			||||||
 | 
					    return getRefControl().getLabelRanges();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** The range of permitted values associated with a label permission. */
 | 
				
			||||||
 | 
					  public PermissionRange getRange(String permission) {
 | 
				
			||||||
 | 
					    return getRefControl().getRange(permission);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Can this user add a patch set to this change? */
 | 
					  /** Can this user add a patch set to this change? */
 | 
				
			||||||
@@ -240,34 +244,22 @@ public class ChangeControl {
 | 
				
			|||||||
      return result;
 | 
					      return result;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final List<PatchSetApproval> allApprovals =
 | 
					    final List<PatchSetApproval> all =
 | 
				
			||||||
        new ArrayList<PatchSetApproval>(db.patchSetApprovals().byPatchSet(
 | 
					        db.patchSetApprovals().byPatchSet(patchSetId).toList();
 | 
				
			||||||
            patchSetId).toList());
 | 
					 | 
				
			||||||
    final PatchSetApproval myAction =
 | 
					 | 
				
			||||||
        ChangeUtil.createSubmitApproval(patchSetId,
 | 
					 | 
				
			||||||
            (IdentifiedUser) getCurrentUser(), db);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    final ApprovalType actionType =
 | 
					 | 
				
			||||||
        approvalTypes.getApprovalType(myAction.getCategoryId());
 | 
					 | 
				
			||||||
    if (actionType == null || !actionType.getCategory().isAction()) {
 | 
					 | 
				
			||||||
      return new CanSubmitResult("Invalid action " + myAction.getCategoryId());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final FunctionState fs =
 | 
					    final FunctionState fs =
 | 
				
			||||||
        functionStateFactory.create(change, patchSetId, allApprovals);
 | 
					        functionStateFactory.create(change, patchSetId, all);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (ApprovalType c : approvalTypes.getApprovalTypes()) {
 | 
					    for (ApprovalType c : approvalTypes.getApprovalTypes()) {
 | 
				
			||||||
      CategoryFunction.forCategory(c.getCategory()).run(c, fs);
 | 
					      CategoryFunction.forCategory(c.getCategory()).run(c, fs);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (!CategoryFunction.forCategory(actionType.getCategory()).isValid(
 | 
					
 | 
				
			||||||
        getCurrentUser(), actionType, fs)) {
 | 
					    for (ApprovalType type : approvalTypes.getApprovalTypes()) {
 | 
				
			||||||
      return new CanSubmitResult(actionType.getCategory().getName()
 | 
					      if (!fs.isValid(type)) {
 | 
				
			||||||
          + " not permitted");
 | 
					        return new CanSubmitResult("Requires " + type.getCategory().getName());
 | 
				
			||||||
    }
 | 
					      }
 | 
				
			||||||
    fs.normalize(actionType, myAction);
 | 
					 | 
				
			||||||
    if (myAction.getValue() <= 0) {
 | 
					 | 
				
			||||||
      return new CanSubmitResult(actionType.getCategory().getName()
 | 
					 | 
				
			||||||
          + " not permitted");
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return CanSubmitResult.OK;
 | 
					    return CanSubmitResult.OK;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,9 +29,6 @@ public interface ProjectCache {
 | 
				
			|||||||
  /** Invalidate the cached information about the given project. */
 | 
					  /** Invalidate the cached information about the given project. */
 | 
				
			||||||
  public void evict(Project p);
 | 
					  public void evict(Project p);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Invalidate the cached information about all projects. */
 | 
					 | 
				
			||||||
  public void evictAll();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** @return sorted iteration of projects. */
 | 
					  /** @return sorted iteration of projects. */
 | 
				
			||||||
  public abstract Iterable<Project.NameKey> all();
 | 
					  public abstract Iterable<Project.NameKey> all();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,9 +14,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.google.gerrit.server.project;
 | 
					package com.google.gerrit.server.project;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.gerrit.reviewdb.AccountGroup;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.Project;
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
import com.google.gerrit.reviewdb.RefRight;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.ReviewDb;
 | 
					import com.google.gerrit.reviewdb.ReviewDb;
 | 
				
			||||||
import com.google.gerrit.server.cache.Cache;
 | 
					import com.google.gerrit.server.cache.Cache;
 | 
				
			||||||
import com.google.gerrit.server.cache.CacheModule;
 | 
					import com.google.gerrit.server.cache.CacheModule;
 | 
				
			||||||
@@ -33,13 +31,9 @@ import com.google.inject.name.Named;
 | 
				
			|||||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
 | 
					import org.eclipse.jgit.errors.RepositoryNotFoundException;
 | 
				
			||||||
import org.eclipse.jgit.lib.Repository;
 | 
					import org.eclipse.jgit.lib.Repository;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.Collection;
 | 
					 | 
				
			||||||
import java.util.Collections;
 | 
					import java.util.Collections;
 | 
				
			||||||
import java.util.HashSet;
 | 
					 | 
				
			||||||
import java.util.Iterator;
 | 
					import java.util.Iterator;
 | 
				
			||||||
import java.util.Map;
 | 
					 | 
				
			||||||
import java.util.NoSuchElementException;
 | 
					import java.util.NoSuchElementException;
 | 
				
			||||||
import java.util.Set;
 | 
					 | 
				
			||||||
import java.util.SortedSet;
 | 
					import java.util.SortedSet;
 | 
				
			||||||
import java.util.TreeSet;
 | 
					import java.util.TreeSet;
 | 
				
			||||||
import java.util.concurrent.locks.Lock;
 | 
					import java.util.concurrent.locks.Lock;
 | 
				
			||||||
@@ -99,11 +93,6 @@ public class ProjectCacheImpl implements ProjectCache {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Invalidate the cached information about all projects. */
 | 
					 | 
				
			||||||
  public void evictAll() {
 | 
					 | 
				
			||||||
    byName.removeAll();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public void onCreateProject(Project.NameKey newProjectName) {
 | 
					  public void onCreateProject(Project.NameKey newProjectName) {
 | 
				
			||||||
    listLock.lock();
 | 
					    listLock.lock();
 | 
				
			||||||
@@ -193,30 +182,7 @@ public class ProjectCacheImpl implements ProjectCache {
 | 
				
			|||||||
        try {
 | 
					        try {
 | 
				
			||||||
          final ProjectConfig cfg = new ProjectConfig(key);
 | 
					          final ProjectConfig cfg = new ProjectConfig(key);
 | 
				
			||||||
          cfg.load(git);
 | 
					          cfg.load(git);
 | 
				
			||||||
 | 
					          return projectStateFactory.create(cfg);
 | 
				
			||||||
          final Project p = cfg.getProject();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          Collection<RefRight> rights = db.refRights().byProject(key).toList();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          Set<AccountGroup.Id> groupIds = new HashSet<AccountGroup.Id>();
 | 
					 | 
				
			||||||
          for (RefRight r : rights) {
 | 
					 | 
				
			||||||
            groupIds.add(r.getAccountGroupId());
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          Map<AccountGroup.Id, AccountGroup> groupsById =
 | 
					 | 
				
			||||||
              db.accountGroups().toMap(db.accountGroups().get(groupIds));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          for (RefRight r : rights) {
 | 
					 | 
				
			||||||
            AccountGroup group = groupsById.get(r.getAccountGroupId());
 | 
					 | 
				
			||||||
            if (group != null) {
 | 
					 | 
				
			||||||
              r.setAccountGroupUUID(group.getGroupUUID());
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
              r.setAccountGroupUUID(new AccountGroup.UUID("DELETED_GROUP_"
 | 
					 | 
				
			||||||
                  + r.getAccountGroupId().get()));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          rights = Collections.unmodifiableCollection(rights);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          return projectStateFactory.create(p, rights);
 | 
					 | 
				
			||||||
        } finally {
 | 
					        } finally {
 | 
				
			||||||
          git.close();
 | 
					          git.close();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,13 +14,15 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.google.gerrit.server.project;
 | 
					package com.google.gerrit.server.project;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static com.google.gerrit.common.CollectionsUtil.*;
 | 
					import static com.google.gerrit.common.CollectionsUtil.isAnyIncludedIn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.AccessSection;
 | 
				
			||||||
 | 
					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.AccountGroup;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.Branch;
 | 
					import com.google.gerrit.reviewdb.Branch;
 | 
				
			||||||
import com.google.gerrit.reviewdb.Change;
 | 
					import com.google.gerrit.reviewdb.Change;
 | 
				
			||||||
import com.google.gerrit.reviewdb.Project;
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
import com.google.gerrit.reviewdb.RefRight;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.CurrentUser;
 | 
					import com.google.gerrit.server.CurrentUser;
 | 
				
			||||||
import com.google.gerrit.server.ReplicationUser;
 | 
					import com.google.gerrit.server.ReplicationUser;
 | 
				
			||||||
import com.google.gerrit.server.config.GitReceivePackGroups;
 | 
					import com.google.gerrit.server.config.GitReceivePackGroups;
 | 
				
			||||||
@@ -29,6 +31,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.util.Collection;
 | 
				
			||||||
import java.util.HashSet;
 | 
					import java.util.HashSet;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -108,6 +111,8 @@ public class ProjectControl {
 | 
				
			|||||||
  private final CurrentUser user;
 | 
					  private final CurrentUser user;
 | 
				
			||||||
  private final ProjectState state;
 | 
					  private final ProjectState state;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private Collection<AccessSection> access;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  ProjectControl(@GitUploadPackGroups Set<AccountGroup.UUID> uploadGroups,
 | 
					  ProjectControl(@GitUploadPackGroups Set<AccountGroup.UUID> uploadGroups,
 | 
				
			||||||
      @GitReceivePackGroups Set<AccountGroup.UUID> receiveGroups,
 | 
					      @GitReceivePackGroups Set<AccountGroup.UUID> receiveGroups,
 | 
				
			||||||
@@ -155,18 +160,18 @@ public class ProjectControl {
 | 
				
			|||||||
  /** Can this user see this project exists? */
 | 
					  /** Can this user see this project exists? */
 | 
				
			||||||
  public boolean isVisible() {
 | 
					  public boolean isVisible() {
 | 
				
			||||||
    return visibleForReplication()
 | 
					    return visibleForReplication()
 | 
				
			||||||
        || canPerformOnAnyRef(ApprovalCategory.READ, (short) 1);
 | 
					        || canPerformOnAnyRef(Permission.READ);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public boolean canAddRefs() {
 | 
					  public boolean canAddRefs() {
 | 
				
			||||||
    return (canPerformOnAnyRef(ApprovalCategory.PUSH_HEAD, ApprovalCategory.PUSH_HEAD_CREATE)
 | 
					    return (canPerformOnAnyRef(Permission.CREATE)
 | 
				
			||||||
        || isOwnerAnyRef());
 | 
					        || isOwnerAnyRef());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Can this user see all the refs in this projects? */
 | 
					  /** Can this user see all the refs in this projects? */
 | 
				
			||||||
  public boolean allRefsAreVisible() {
 | 
					  public boolean allRefsAreVisible() {
 | 
				
			||||||
    return visibleForReplication()
 | 
					    return visibleForReplication()
 | 
				
			||||||
        || canPerformOnAllRefs(ApprovalCategory.READ, (short) 1);
 | 
					        || canPerformOnAllRefs(Permission.READ);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Is this project completely visible for replication? */
 | 
					  /** Is this project completely visible for replication? */
 | 
				
			||||||
@@ -177,49 +182,60 @@ public class ProjectControl {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /** Is this user a project owner? Ownership does not imply {@link #isVisible()} */
 | 
					  /** Is this user a project owner? Ownership does not imply {@link #isVisible()} */
 | 
				
			||||||
  public boolean isOwner() {
 | 
					  public boolean isOwner() {
 | 
				
			||||||
    return controlForRef(RefRight.ALL).isOwner()
 | 
					    return controlForRef(AccessSection.ALL).isOwner()
 | 
				
			||||||
        || getCurrentUser().isAdministrator();
 | 
					        || getCurrentUser().isAdministrator();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Does this user have ownership on at least one reference name? */
 | 
					  /** Does this user have ownership on at least one reference name? */
 | 
				
			||||||
  public boolean isOwnerAnyRef() {
 | 
					  public boolean isOwnerAnyRef() {
 | 
				
			||||||
    return canPerformOnAnyRef(ApprovalCategory.OWN, (short) 1)
 | 
					    return canPerformOnAnyRef(Permission.OWNER)
 | 
				
			||||||
        || getCurrentUser().isAdministrator();
 | 
					        || getCurrentUser().isAdministrator();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** @return true if the user can upload to at least one reference */
 | 
					  /** @return true if the user can upload to at least one reference */
 | 
				
			||||||
  public boolean canPushToAtLeastOneRef() {
 | 
					  public boolean canPushToAtLeastOneRef() {
 | 
				
			||||||
    return canPerformOnAnyRef(ApprovalCategory.READ, (short) 2)
 | 
					    return canPerformOnAnyRef(Permission.PUSH)
 | 
				
			||||||
        || canPerformOnAnyRef(ApprovalCategory.PUSH_HEAD, (short) 1)
 | 
					        || canPerformOnAnyRef(Permission.PUSH_TAG);
 | 
				
			||||||
        || canPerformOnAnyRef(ApprovalCategory.PUSH_TAG, (short) 1);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // TODO (anatol.pomazau): Try to merge this method with similar RefRightsForPattern#canPerform
 | 
					  private boolean canPerformOnAnyRef(String permissionName) {
 | 
				
			||||||
  private boolean canPerformOnAnyRef(ApprovalCategory.Id actionId,
 | 
					 | 
				
			||||||
      short requireValue) {
 | 
					 | 
				
			||||||
    final Set<AccountGroup.UUID> groups = user.getEffectiveGroups();
 | 
					    final Set<AccountGroup.UUID> groups = user.getEffectiveGroups();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (final RefRight pr : state.getAllRights(actionId, true)) {
 | 
					    for (AccessSection section : access()) {
 | 
				
			||||||
      if (groups.contains(pr.getAccountGroupUUID())
 | 
					      Permission permission = section.getPermission(permissionName);
 | 
				
			||||||
          && pr.getMaxValue() >= requireValue) {
 | 
					      if (permission == null) {
 | 
				
			||||||
        return true;
 | 
					        continue;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (PermissionRule rule : permission.getRules()) {
 | 
				
			||||||
 | 
					        if (rule.getDeny()) {
 | 
				
			||||||
 | 
					          continue;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Being in a group that was granted this permission is only an
 | 
				
			||||||
 | 
					        // approximation.  There might be overrides and doNotInherit
 | 
				
			||||||
 | 
					        // that would render this to be false.
 | 
				
			||||||
 | 
					        //
 | 
				
			||||||
 | 
					        if (groups.contains(rule.getGroup().getUUID())
 | 
				
			||||||
 | 
					            && controlForRef(section.getRefPattern()).canPerform(permissionName)) {
 | 
				
			||||||
 | 
					          return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private boolean canPerformOnAllRefs(ApprovalCategory.Id actionId,
 | 
					  private boolean canPerformOnAllRefs(String permission) {
 | 
				
			||||||
      short requireValue) {
 | 
					 | 
				
			||||||
    boolean canPerform = false;
 | 
					    boolean canPerform = false;
 | 
				
			||||||
    final Set<String> patterns = allRefPatterns(actionId);
 | 
					    Set<String> patterns = allRefPatterns(permission);
 | 
				
			||||||
    if (patterns.contains(RefRight.ALL)) {
 | 
					    if (patterns.contains(AccessSection.ALL)) {
 | 
				
			||||||
      // Only possible if granted on the pattern that
 | 
					      // Only possible if granted on the pattern that
 | 
				
			||||||
      // matches every possible reference.  Check all
 | 
					      // matches every possible reference.  Check all
 | 
				
			||||||
      // patterns also have the permission.
 | 
					      // patterns also have the permission.
 | 
				
			||||||
      //
 | 
					      //
 | 
				
			||||||
      for (final String pattern : patterns) {
 | 
					      for (final String pattern : patterns) {
 | 
				
			||||||
        if (controlForRef(pattern).canPerform(actionId, requireValue)) {
 | 
					        if (controlForRef(pattern).canPerform(permission)) {
 | 
				
			||||||
          canPerform = true;
 | 
					          canPerform = true;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          return false;
 | 
					          return false;
 | 
				
			||||||
@@ -229,14 +245,24 @@ public class ProjectControl {
 | 
				
			|||||||
    return canPerform;
 | 
					    return canPerform;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private Set<String> allRefPatterns(ApprovalCategory.Id actionId) {
 | 
					  private Set<String> allRefPatterns(String permissionName) {
 | 
				
			||||||
    final Set<String> all = new HashSet<String>();
 | 
					    Set<String> all = new HashSet<String>();
 | 
				
			||||||
    for (final RefRight pr : state.getAllRights(actionId, true)) {
 | 
					    for (AccessSection section : access()) {
 | 
				
			||||||
      all.add(pr.getRefPattern());
 | 
					      Permission permission = section.getPermission(permissionName);
 | 
				
			||||||
 | 
					      if (permission != null) {
 | 
				
			||||||
 | 
					        all.add(section.getRefPattern());
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return all;
 | 
					    return all;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  Collection<AccessSection> access() {
 | 
				
			||||||
 | 
					    if (access == null) {
 | 
				
			||||||
 | 
					      access = state.getAllAccessSections();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return access;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public boolean canRunUploadPack() {
 | 
					  public boolean canRunUploadPack() {
 | 
				
			||||||
    return isAnyIncludedIn(uploadGroups, user.getEffectiveGroups());
 | 
					    return isAnyIncludedIn(uploadGroups, user.getEffectiveGroups());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,13 +14,16 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.google.gerrit.server.project;
 | 
					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.AccountGroup;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.Project;
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
import com.google.gerrit.reviewdb.RefRight;
 | 
					 | 
				
			||||||
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.config.WildProjectName;
 | 
					import com.google.gerrit.server.config.WildProjectName;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.git.ProjectConfig;
 | 
				
			||||||
import com.google.inject.Inject;
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
import com.google.inject.assistedinject.Assisted;
 | 
					import com.google.inject.assistedinject.Assisted;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -28,15 +31,13 @@ import java.util.ArrayList;
 | 
				
			|||||||
import java.util.Collection;
 | 
					import java.util.Collection;
 | 
				
			||||||
import java.util.Collections;
 | 
					import java.util.Collections;
 | 
				
			||||||
import java.util.HashSet;
 | 
					import java.util.HashSet;
 | 
				
			||||||
import java.util.Iterator;
 | 
					 | 
				
			||||||
import java.util.LinkedList;
 | 
					 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Cached information on a project. */
 | 
					/** Cached information on a project. */
 | 
				
			||||||
public class ProjectState {
 | 
					public class ProjectState {
 | 
				
			||||||
  public interface Factory {
 | 
					  public interface Factory {
 | 
				
			||||||
    ProjectState create(Project project, Collection<RefRight> localRights);
 | 
					    ProjectState create(ProjectConfig config);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private final AnonymousUser anonymousUser;
 | 
					  private final AnonymousUser anonymousUser;
 | 
				
			||||||
@@ -44,92 +45,64 @@ public class ProjectState {
 | 
				
			|||||||
  private final ProjectCache projectCache;
 | 
					  private final ProjectCache projectCache;
 | 
				
			||||||
  private final ProjectControl.AssistedFactory projectControlFactory;
 | 
					  private final ProjectControl.AssistedFactory projectControlFactory;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private final Project project;
 | 
					  private final ProjectConfig config;
 | 
				
			||||||
  private final Collection<RefRight> localRights;
 | 
					 | 
				
			||||||
  private final Set<AccountGroup.UUID> localOwners;
 | 
					  private final Set<AccountGroup.UUID> localOwners;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private volatile Collection<RefRight> inheritedRights;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  protected ProjectState(final AnonymousUser anonymousUser,
 | 
					  protected ProjectState(final AnonymousUser anonymousUser,
 | 
				
			||||||
      final ProjectCache projectCache,
 | 
					      final ProjectCache projectCache,
 | 
				
			||||||
      @WildProjectName final Project.NameKey wildProject,
 | 
					      @WildProjectName final Project.NameKey wildProject,
 | 
				
			||||||
      final ProjectControl.AssistedFactory projectControlFactory,
 | 
					      final ProjectControl.AssistedFactory projectControlFactory,
 | 
				
			||||||
      @Assisted final Project project,
 | 
					      @Assisted final ProjectConfig config) {
 | 
				
			||||||
      @Assisted Collection<RefRight> rights) {
 | 
					 | 
				
			||||||
    this.anonymousUser = anonymousUser;
 | 
					    this.anonymousUser = anonymousUser;
 | 
				
			||||||
    this.projectCache = projectCache;
 | 
					    this.projectCache = projectCache;
 | 
				
			||||||
    this.wildProject = wildProject;
 | 
					    this.wildProject = wildProject;
 | 
				
			||||||
    this.projectControlFactory = projectControlFactory;
 | 
					    this.projectControlFactory = projectControlFactory;
 | 
				
			||||||
 | 
					    this.config = config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (wildProject.equals(project.getNameKey())) {
 | 
					    HashSet<AccountGroup.UUID> groups = new HashSet<AccountGroup.UUID>();
 | 
				
			||||||
      rights = new ArrayList<RefRight>(rights);
 | 
					    AccessSection all = config.getAccessSection(AccessSection.ALL);
 | 
				
			||||||
      for (Iterator<RefRight> itr = rights.iterator(); itr.hasNext();) {
 | 
					    if (all != null) {
 | 
				
			||||||
        if (!itr.next().getApprovalCategoryId().canBeOnWildProject()) {
 | 
					      Permission owner = all.getPermission(Permission.OWNER);
 | 
				
			||||||
          itr.remove();
 | 
					      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);
 | 
					    localOwners = Collections.unmodifiableSet(groups);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public Project getProject() {
 | 
					  public Project getProject() {
 | 
				
			||||||
    return project;
 | 
					    return getConfig().getProject();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  public ProjectConfig getConfig() {
 | 
				
			||||||
 | 
					    return config;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Get the rights that pertain only to this project. */
 | 
					  /** Get the rights that pertain only to this project. */
 | 
				
			||||||
  public Collection<RefRight> getLocalRights() {
 | 
					  public Collection<AccessSection> getLocalAccessSections() {
 | 
				
			||||||
    return localRights;
 | 
					    return getConfig().getAccessSections();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /** Get the rights this project inherits. */
 | 
				
			||||||
   * Get the rights that pertain only to this project.
 | 
					  public Collection<AccessSection> getInheritedAccessSections() {
 | 
				
			||||||
   *
 | 
					    if (isWildProject()) {
 | 
				
			||||||
   * @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()) {
 | 
					 | 
				
			||||||
      return Collections.emptyList();
 | 
					      return Collections.emptyList();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    List<RefRight> inherited = new ArrayList<RefRight>();
 | 
					    List<AccessSection> inherited = new ArrayList<AccessSection>();
 | 
				
			||||||
    Set<Project.NameKey> seen = new HashSet<Project.NameKey>();
 | 
					    Set<Project.NameKey> seen = new HashSet<Project.NameKey>();
 | 
				
			||||||
    Project.NameKey parent = project.getParent();
 | 
					    Project.NameKey parent = getProject().getParent();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    while (parent != null && seen.add(parent)) {
 | 
					    while (parent != null && seen.add(parent)) {
 | 
				
			||||||
      ProjectState s = projectCache.get(parent);
 | 
					      ProjectState s = projectCache.get(parent);
 | 
				
			||||||
      if (s != null) {
 | 
					      if (s != null) {
 | 
				
			||||||
        inherited.addAll(s.getLocalRights());
 | 
					        inherited.addAll(s.getLocalAccessSections());
 | 
				
			||||||
        parent = s.getProject().getParent();
 | 
					        parent = s.getProject().getParent();
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
@@ -138,76 +111,21 @@ public class ProjectState {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // Wild project is the parent, or the root of the tree
 | 
					    // Wild project is the parent, or the root of the tree
 | 
				
			||||||
    if (parent == null) {
 | 
					    if (parent == null) {
 | 
				
			||||||
      inherited.addAll(getWildProjectRights());
 | 
					      ProjectState s = projectCache.get(wildProject);
 | 
				
			||||||
    }
 | 
					      if (s != null) {
 | 
				
			||||||
 | 
					        inherited.addAll(s.getLocalAccessSections());
 | 
				
			||||||
    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);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return Collections.unmodifiableCollection(rights);
 | 
					
 | 
				
			||||||
 | 
					    return inherited;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Is this the special wild project which manages inherited rights? */
 | 
					  /** Get both local and inherited access sections. */
 | 
				
			||||||
  public boolean isSpecialWildProject() {
 | 
					  public Collection<AccessSection> getAllAccessSections() {
 | 
				
			||||||
    return project.getNameKey().equals(wildProject);
 | 
					    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
 | 
					   *         that has local owners are returned
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public Set<AccountGroup.UUID> getOwners() {
 | 
					  public Set<AccountGroup.UUID> getOwners() {
 | 
				
			||||||
    if (!localOwners.isEmpty() || isSpecialWildProject()
 | 
					    Project.NameKey parentName = getProject().getParent();
 | 
				
			||||||
        || project.getParent() == null) {
 | 
					    if (!localOwners.isEmpty() || parentName == null || isWildProject()) {
 | 
				
			||||||
      return localOwners;
 | 
					      return localOwners;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final ProjectState parent = projectCache.get(project.getParent());
 | 
					    ProjectState parent = projectCache.get(parentName);
 | 
				
			||||||
    if (parent != null) {
 | 
					    if (parent != null) {
 | 
				
			||||||
      return parent.getOwners();
 | 
					      return parent.getOwners();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -238,12 +156,22 @@ public class ProjectState {
 | 
				
			|||||||
   *         assigned for one of the parent projects (the inherited owners).
 | 
					   *         assigned for one of the parent projects (the inherited owners).
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public Set<AccountGroup.UUID> getAllOwners() {
 | 
					  public Set<AccountGroup.UUID> getAllOwners() {
 | 
				
			||||||
    final HashSet<AccountGroup.UUID> owners = new HashSet<AccountGroup.UUID>();
 | 
					    HashSet<AccountGroup.UUID> owners = new HashSet<AccountGroup.UUID>();
 | 
				
			||||||
    for (final RefRight right : getAllRights(ApprovalCategory.OWN, true)) {
 | 
					    owners.addAll(localOwners);
 | 
				
			||||||
      if (right.getMaxValue() > 0 && right.getRefPattern().equals(RefRight.ALL)) {
 | 
					
 | 
				
			||||||
        owners.add(right.getAccountGroupUUID());
 | 
					    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);
 | 
					    return Collections.unmodifiableSet(owners);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -255,20 +183,7 @@ public class ProjectState {
 | 
				
			|||||||
    return projectControlFactory.create(user, this);
 | 
					    return projectControlFactory.create(user, this);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static Collection<RefRight> filter(Collection<RefRight> all,
 | 
					  private boolean isWildProject() {
 | 
				
			||||||
      ApprovalCategory.Id actionId) {
 | 
					    return wildProject.equals(getProject().getNameKey());
 | 
				
			||||||
    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);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,25 +14,13 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.google.gerrit.server.project;
 | 
					package com.google.gerrit.server.project;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.FORGE_AUTHOR;
 | 
					import com.google.gerrit.common.CollectionsUtil;
 | 
				
			||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.FORGE_COMMITTER;
 | 
					import com.google.gerrit.common.data.AccessSection;
 | 
				
			||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.FORGE_IDENTITY;
 | 
					 | 
				
			||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.FORGE_SERVER;
 | 
					 | 
				
			||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.OWN;
 | 
					 | 
				
			||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_HEAD;
 | 
					 | 
				
			||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_HEAD_CREATE;
 | 
					 | 
				
			||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_HEAD_REPLACE;
 | 
					 | 
				
			||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_HEAD_UPDATE;
 | 
					 | 
				
			||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_TAG;
 | 
					 | 
				
			||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_TAG_ANNOTATED;
 | 
					 | 
				
			||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.PUSH_TAG_SIGNED;
 | 
					 | 
				
			||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.READ;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.gerrit.common.data.ParamertizedString;
 | 
					import com.google.gerrit.common.data.ParamertizedString;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.Permission;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.PermissionRange;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.PermissionRule;
 | 
				
			||||||
import com.google.gerrit.reviewdb.AccountGroup;
 | 
					import com.google.gerrit.reviewdb.AccountGroup;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.RefRight;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.SystemConfig;
 | 
					 | 
				
			||||||
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.inject.Inject;
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
@@ -49,7 +37,6 @@ import org.eclipse.jgit.revwalk.RevWalk;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import java.io.IOException;
 | 
					import java.io.IOException;
 | 
				
			||||||
import java.util.ArrayList;
 | 
					import java.util.ArrayList;
 | 
				
			||||||
import java.util.Collection;
 | 
					 | 
				
			||||||
import java.util.Collections;
 | 
					import java.util.Collections;
 | 
				
			||||||
import java.util.Comparator;
 | 
					import java.util.Comparator;
 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.HashMap;
 | 
				
			||||||
@@ -57,8 +44,6 @@ 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 java.util.SortedMap;
 | 
					 | 
				
			||||||
import java.util.TreeMap;
 | 
					 | 
				
			||||||
import java.util.regex.Pattern;
 | 
					import java.util.regex.Pattern;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -71,6 +56,9 @@ public class RefControl {
 | 
				
			|||||||
  private final ProjectControl projectControl;
 | 
					  private final ProjectControl projectControl;
 | 
				
			||||||
  private final String refName;
 | 
					  private final String refName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private Map<String, List<PermissionRule>> permissions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private Boolean owner;
 | 
				
			||||||
  private Boolean canForgeAuthor;
 | 
					  private Boolean canForgeAuthor;
 | 
				
			||||||
  private Boolean canForgeCommitter;
 | 
					  private Boolean canForgeCommitter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -111,26 +99,29 @@ public class RefControl {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /** Is this user a ref owner? */
 | 
					  /** Is this user a ref owner? */
 | 
				
			||||||
  public boolean isOwner() {
 | 
					  public boolean isOwner() {
 | 
				
			||||||
    if (canPerform(OWN, (short) 1)) {
 | 
					    if (owner == null) {
 | 
				
			||||||
      return true;
 | 
					      if (canPerform(Permission.OWNER)) {
 | 
				
			||||||
    }
 | 
					        owner = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // We have to prevent infinite recursion here, the project control
 | 
					      } else if (getRefName().equals(
 | 
				
			||||||
    // calls us to find out if there is ownership of all references in
 | 
					          AccessSection.ALL.substring(0, AccessSection.ALL.length() - 1))) {
 | 
				
			||||||
    // order to determine project level ownership.
 | 
					        // We have to prevent infinite recursion here, the project control
 | 
				
			||||||
    //
 | 
					        // calls us to find out if there is ownership of all references in
 | 
				
			||||||
    if (getRefName().equals(
 | 
					        // order to determine project level ownership.
 | 
				
			||||||
        RefRight.ALL.substring(0, RefRight.ALL.length() - 1))) {
 | 
					        //
 | 
				
			||||||
      return getCurrentUser().isAdministrator();
 | 
					        owner = getCurrentUser().isAdministrator();
 | 
				
			||||||
    } else {
 | 
					
 | 
				
			||||||
      return getProjectControl().isOwner();
 | 
					      } else {
 | 
				
			||||||
 | 
					        owner = getProjectControl().isOwner();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return owner;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Can this user see this reference exists? */
 | 
					  /** Can this user see this reference exists? */
 | 
				
			||||||
  public boolean isVisible() {
 | 
					  public boolean isVisible() {
 | 
				
			||||||
    return getProjectControl().visibleForReplication()
 | 
					    return getProjectControl().visibleForReplication()
 | 
				
			||||||
        || canPerform(READ, (short) 1);
 | 
					        || canPerform(Permission.READ);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -141,27 +132,40 @@ public class RefControl {
 | 
				
			|||||||
   *         ref
 | 
					   *         ref
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public boolean canUpload() {
 | 
					  public boolean canUpload() {
 | 
				
			||||||
    return canPerform(READ, (short) 2);
 | 
					    return getProjectControl()
 | 
				
			||||||
 | 
					        .controlForRef("refs/for/" + getRefName())
 | 
				
			||||||
 | 
					        .canPerform(Permission.PUSH);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** @return true if this user can submit merge patch sets to this ref */
 | 
					  /** @return true if this user can submit merge patch sets to this ref */
 | 
				
			||||||
  public boolean canUploadMerges() {
 | 
					  public boolean canUploadMerges() {
 | 
				
			||||||
    return canPerform(READ, (short) 3);
 | 
					    return getProjectControl()
 | 
				
			||||||
 | 
					      .controlForRef("refs/for/" + getRefName())
 | 
				
			||||||
 | 
					      .canPerform(Permission.PUSH_MERGE);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** @return true if this user can submit patch sets to this ref */
 | 
					  /** @return true if this user can submit patch sets to this ref */
 | 
				
			||||||
  public boolean canSubmit() {
 | 
					  public boolean canSubmit() {
 | 
				
			||||||
    return canPerform(ApprovalCategory.SUBMIT, (short) 1);
 | 
					    return canPerform(Permission.SUBMIT);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** @return true if the user can update the reference as a fast-forward. */
 | 
					  /** @return true if the user can update the reference as a fast-forward. */
 | 
				
			||||||
  public boolean canUpdate() {
 | 
					  public boolean canUpdate() {
 | 
				
			||||||
    return canPerform(PUSH_HEAD, PUSH_HEAD_UPDATE);
 | 
					    return canPerform(Permission.PUSH);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** @return true if the user can rewind (force push) the reference. */
 | 
					  /** @return true if the user can rewind (force push) the reference. */
 | 
				
			||||||
  public boolean canForceUpdate() {
 | 
					  public boolean canForceUpdate() {
 | 
				
			||||||
    return canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE) || canDelete();
 | 
					    return canPushWithForce() || canDelete();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private boolean canPushWithForce() {
 | 
				
			||||||
 | 
					    for (PermissionRule rule : access(Permission.PUSH)) {
 | 
				
			||||||
 | 
					      if (rule.getForce()) {
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
@@ -183,7 +187,7 @@ public class RefControl {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (object instanceof RevCommit) {
 | 
					    if (object instanceof RevCommit) {
 | 
				
			||||||
      return owner || canPerform(PUSH_HEAD, PUSH_HEAD_CREATE);
 | 
					      return owner || canPerform(Permission.CREATE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    } else if (object instanceof RevTag) {
 | 
					    } else if (object instanceof RevTag) {
 | 
				
			||||||
      final RevTag tag = (RevTag) object;
 | 
					      final RevTag tag = (RevTag) object;
 | 
				
			||||||
@@ -205,7 +209,7 @@ public class RefControl {
 | 
				
			|||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          valid = false;
 | 
					          valid = false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (!valid && !owner && !canPerform(FORGE_IDENTITY, FORGE_COMMITTER)) {
 | 
					        if (!valid && !owner && !canForgeCommitter()) {
 | 
				
			||||||
          return false;
 | 
					          return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -214,9 +218,9 @@ public class RefControl {
 | 
				
			|||||||
      // than if it doesn't have a PGP signature.
 | 
					      // than if it doesn't have a PGP signature.
 | 
				
			||||||
      //
 | 
					      //
 | 
				
			||||||
      if (tag.getFullMessage().contains("-----BEGIN PGP SIGNATURE-----\n")) {
 | 
					      if (tag.getFullMessage().contains("-----BEGIN PGP SIGNATURE-----\n")) {
 | 
				
			||||||
        return owner || canPerform(PUSH_TAG, PUSH_TAG_SIGNED);
 | 
					        return owner || canPerform(Permission.PUSH_TAG);
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        return owner || canPerform(PUSH_TAG, PUSH_TAG_ANNOTATED);
 | 
					        return owner || canPerform(Permission.PUSH_TAG);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
@@ -233,10 +237,10 @@ public class RefControl {
 | 
				
			|||||||
  public boolean canDelete() {
 | 
					  public boolean canDelete() {
 | 
				
			||||||
    switch (getCurrentUser().getAccessPath()) {
 | 
					    switch (getCurrentUser().getAccessPath()) {
 | 
				
			||||||
      case WEB_UI:
 | 
					      case WEB_UI:
 | 
				
			||||||
        return isOwner() || canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE);
 | 
					        return isOwner() || canPushWithForce();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      case GIT:
 | 
					      case GIT:
 | 
				
			||||||
        return canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE);
 | 
					        return canPushWithForce();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        return false;
 | 
					        return false;
 | 
				
			||||||
@@ -246,7 +250,7 @@ public class RefControl {
 | 
				
			|||||||
  /** @return true if this user can forge the author line in a commit. */
 | 
					  /** @return true if this user can forge the author line in a commit. */
 | 
				
			||||||
  public boolean canForgeAuthor() {
 | 
					  public boolean canForgeAuthor() {
 | 
				
			||||||
    if (canForgeAuthor == null) {
 | 
					    if (canForgeAuthor == null) {
 | 
				
			||||||
      canForgeAuthor = canPerform(FORGE_IDENTITY, FORGE_AUTHOR);
 | 
					      canForgeAuthor = canPerform(Permission.FORGE_AUTHOR);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return canForgeAuthor;
 | 
					    return canForgeAuthor;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -254,314 +258,103 @@ public class RefControl {
 | 
				
			|||||||
  /** @return true if this user can forge the committer line in a commit. */
 | 
					  /** @return true if this user can forge the committer line in a commit. */
 | 
				
			||||||
  public boolean canForgeCommitter() {
 | 
					  public boolean canForgeCommitter() {
 | 
				
			||||||
    if (canForgeCommitter == null) {
 | 
					    if (canForgeCommitter == null) {
 | 
				
			||||||
      canForgeCommitter = canPerform(FORGE_IDENTITY, FORGE_COMMITTER);
 | 
					      canForgeCommitter = canPerform(Permission.FORGE_COMMITTER);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return canForgeCommitter;
 | 
					    return canForgeCommitter;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** @return true if this user can forge the server on the committer line. */
 | 
					  /** @return true if this user can forge the server on the committer line. */
 | 
				
			||||||
  public boolean canForgeGerritServerIdentity() {
 | 
					  public boolean canForgeGerritServerIdentity() {
 | 
				
			||||||
    return canPerform(FORGE_IDENTITY, FORGE_SERVER);
 | 
					    return canPerform(Permission.FORGE_SERVER);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public short normalize(ApprovalCategory.Id category, short score) {
 | 
					  /** All value ranges of any allowed label permission. */
 | 
				
			||||||
    short minAllowed = 0, maxAllowed = 0;
 | 
					  public List<PermissionRange> getLabelRanges() {
 | 
				
			||||||
    for (RefRight r : getApplicableRights(category)) {
 | 
					    List<PermissionRange> r = new ArrayList<PermissionRange>();
 | 
				
			||||||
      if (getCurrentUser().getEffectiveGroups().contains(r.getAccountGroupUUID())) {
 | 
					    for (Map.Entry<String, List<PermissionRule>> e : permissions().entrySet()) {
 | 
				
			||||||
        minAllowed = (short) Math.min(minAllowed, r.getMinValue());
 | 
					      if (Permission.isLabel(e.getKey())) {
 | 
				
			||||||
        maxAllowed = (short) Math.max(maxAllowed, r.getMaxValue());
 | 
					        r.add(toRange(e.getKey(), e.getValue()));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return r;
 | 
				
			||||||
    if (score < minAllowed) {
 | 
					 | 
				
			||||||
      score = minAllowed;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    if (score > maxAllowed) {
 | 
					 | 
				
			||||||
      score = maxAllowed;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return score;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /** The range of permitted values associated with a label permission. */
 | 
				
			||||||
   * Convenience holder class used to map a ref pattern to the list of
 | 
					  public PermissionRange getRange(String permission) {
 | 
				
			||||||
   * {@code RefRight}s that use it in the database.
 | 
					    if (Permission.isLabel(permission)) {
 | 
				
			||||||
   */
 | 
					      return toRange(permission, access(permission));
 | 
				
			||||||
  public final static class RefRightsForPattern {
 | 
					 | 
				
			||||||
    private final List<RefRight> rights;
 | 
					 | 
				
			||||||
    private boolean containsExclusive;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public RefRightsForPattern() {
 | 
					 | 
				
			||||||
      rights = new ArrayList<RefRight>();
 | 
					 | 
				
			||||||
      containsExclusive = false;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public void addRight(RefRight right) {
 | 
					  private static PermissionRange toRange(String permissionName, List<PermissionRule> ruleList) {
 | 
				
			||||||
      rights.add(right);
 | 
					    int min = 0;
 | 
				
			||||||
      if (right.isExclusive()) {
 | 
					    int max = 0;
 | 
				
			||||||
        containsExclusive = true;
 | 
					    for (PermissionRule rule : ruleList) {
 | 
				
			||||||
      }
 | 
					      min = Math.min(min, rule.getMin());
 | 
				
			||||||
 | 
					      max = Math.max(max, rule.getMax());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    return new PermissionRange(permissionName, min, max);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public List<RefRight> getRights() {
 | 
					  /** True if the user has this permission. Works only for non labels. */
 | 
				
			||||||
      return Collections.unmodifiableList(rights);
 | 
					  boolean canPerform(String permissionName) {
 | 
				
			||||||
    }
 | 
					    return !access(permissionName).isEmpty();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public boolean containsExclusive() {
 | 
					  /** Rules for the given permission, or the empty list. */
 | 
				
			||||||
      return containsExclusive;
 | 
					  private List<PermissionRule> access(String permissionName) {
 | 
				
			||||||
    }
 | 
					    List<PermissionRule> r = permissions().get(permissionName);
 | 
				
			||||||
 | 
					    return r != null ? r : Collections.<PermissionRule> emptyList();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					  /** All rules that pertain to this user, on this reference. */
 | 
				
			||||||
     * Returns The max allowed value for this ref pattern for all specified
 | 
					  private Map<String, List<PermissionRule>> permissions() {
 | 
				
			||||||
     * groups.
 | 
					    if (permissions == null) {
 | 
				
			||||||
     *
 | 
					      List<AccessSection> sections = new ArrayList<AccessSection>();
 | 
				
			||||||
     * @param groups The groups of the user
 | 
					      for (AccessSection section : projectControl.access()) {
 | 
				
			||||||
     * @return The allowed value for this ref for all the specified groups
 | 
					        if (appliesToRef(section)) {
 | 
				
			||||||
     */
 | 
					          sections.add(section);
 | 
				
			||||||
    private boolean allowedValueForRef(Set<AccountGroup.UUID> groups, short level) {
 | 
					 | 
				
			||||||
      for (RefRight right : rights) {
 | 
					 | 
				
			||||||
        if (groups.contains(right.getAccountGroupUUID())
 | 
					 | 
				
			||||||
            && right.getMaxValue() >= level) {
 | 
					 | 
				
			||||||
          return true;
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return false;
 | 
					      Collections.sort(sections, new MostSpecificComparator(getRefName()));
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  boolean canPerform(ApprovalCategory.Id actionId, short level) {
 | 
					      Set<SeenRule> seen = new HashSet<SeenRule>();
 | 
				
			||||||
    final Set<AccountGroup.UUID> groups = getCurrentUser().getEffectiveGroups();
 | 
					      Set<String> exclusiveGroupPermissions = new HashSet<String>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    List<RefRight> allRights = new ArrayList<RefRight>();
 | 
					      permissions = new HashMap<String, List<PermissionRule>>();
 | 
				
			||||||
    allRights.addAll(getAllRights(actionId));
 | 
					      for (AccessSection section : sections) {
 | 
				
			||||||
 | 
					        for (Permission permission : section.getPermissions()) {
 | 
				
			||||||
 | 
					          if (exclusiveGroupPermissions.contains(permission.getName())) {
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SortedMap<String, RefRightsForPattern> perPatternRights =
 | 
					          for (PermissionRule rule : permission.getRules()) {
 | 
				
			||||||
      sortedRightsByPattern(allRights);
 | 
					            if (matchGroup(rule.getGroup().getUUID())) {
 | 
				
			||||||
 | 
					              SeenRule s = new SeenRule(section, permission, rule);
 | 
				
			||||||
    for (RefRightsForPattern right : perPatternRights.values()) {
 | 
					              if (seen.add(s) && !rule.getDeny()) {
 | 
				
			||||||
      if (right.allowedValueForRef(groups, level)) {
 | 
					                List<PermissionRule> r = permissions.get(permission.getName());
 | 
				
			||||||
        return true;
 | 
					                if (r == null) {
 | 
				
			||||||
      }
 | 
					                  r = new ArrayList<PermissionRule>(2);
 | 
				
			||||||
      if (right.containsExclusive() && !actionId.equals(OWN)) {
 | 
					                  permissions.put(permission.getName(), r);
 | 
				
			||||||
        break;
 | 
					                }
 | 
				
			||||||
      }
 | 
					                r.add(rule);
 | 
				
			||||||
    }
 | 
					              }
 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Order the Ref Pattern by the most specific. This sort is done by:
 | 
					 | 
				
			||||||
   * <ul>
 | 
					 | 
				
			||||||
   * <li>1 - The minor value of Levenshtein string distance between the branch
 | 
					 | 
				
			||||||
   * name and the regex string shortest example. A shorter distance is a more
 | 
					 | 
				
			||||||
   * specific match.
 | 
					 | 
				
			||||||
   * <li>2 - Finites first, infinities after.
 | 
					 | 
				
			||||||
   * <li>3 - Number of transitions.
 | 
					 | 
				
			||||||
   * <li>4 - Length of the expression text.
 | 
					 | 
				
			||||||
   * </ul>
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * Levenshtein distance is a measure of the similarity between two strings.
 | 
					 | 
				
			||||||
   * The distance is the number of deletions, insertions, or substitutions
 | 
					 | 
				
			||||||
   * required to transform one string into another.
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * For example, if given refs/heads/m* and refs/heads/*, the distances are 5
 | 
					 | 
				
			||||||
   * and 6. It means that refs/heads/m* is more specific because it's closer to
 | 
					 | 
				
			||||||
   * refs/heads/master than refs/heads/*.
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * Another example could be refs/heads/* and refs/heads/[a-zA-Z]*, the
 | 
					 | 
				
			||||||
   * distances are both 6. Both are infinite, but refs/heads/[a-zA-Z]* has more
 | 
					 | 
				
			||||||
   * transitions, which after all turns it more specific.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  private final Comparator<String> BY_MOST_SPECIFIC_SORT =
 | 
					 | 
				
			||||||
      new Comparator<String>() {
 | 
					 | 
				
			||||||
        public int compare(final String pattern1, final String pattern2) {
 | 
					 | 
				
			||||||
          int cmp = distance(pattern1) - distance(pattern2);
 | 
					 | 
				
			||||||
          if (cmp == 0) {
 | 
					 | 
				
			||||||
            boolean p1_finite = finite(pattern1);
 | 
					 | 
				
			||||||
            boolean p2_finite = finite(pattern2);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (p1_finite && !p2_finite) {
 | 
					 | 
				
			||||||
              cmp = -1;
 | 
					 | 
				
			||||||
            } else if (!p1_finite && p2_finite) {
 | 
					 | 
				
			||||||
              cmp = 1;
 | 
					 | 
				
			||||||
            } else /* if (f1 == f2) */{
 | 
					 | 
				
			||||||
              cmp = 0;
 | 
					 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
          if (cmp == 0) {
 | 
					 | 
				
			||||||
            cmp = transitions(pattern1) - transitions(pattern2);
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          if (cmp == 0) {
 | 
					 | 
				
			||||||
            cmp = pattern2.length() - pattern1.length();
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          return cmp;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private int distance(String pattern) {
 | 
					          if (permission.getExclusiveGroup()) {
 | 
				
			||||||
          String example;
 | 
					            exclusiveGroupPermissions.add(permission.getName());
 | 
				
			||||||
          if (isRE(pattern)) {
 | 
					 | 
				
			||||||
            example = shortestExample(pattern);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          } else if (pattern.endsWith("/*")) {
 | 
					 | 
				
			||||||
            example = pattern.substring(0, pattern.length() - 1) + '1';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          } else if (pattern.equals(getRefName())) {
 | 
					 | 
				
			||||||
            return 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            return Math.max(pattern.length(), getRefName().length());
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
          return StringUtils.getLevenshteinDistance(example, getRefName());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private boolean finite(String pattern) {
 | 
					 | 
				
			||||||
          if (isRE(pattern)) {
 | 
					 | 
				
			||||||
            return toRegExp(pattern).toAutomaton().isFinite();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          } else if (pattern.endsWith("/*")) {
 | 
					 | 
				
			||||||
            return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            return true;
 | 
					 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					 | 
				
			||||||
        private int transitions(String pattern) {
 | 
					 | 
				
			||||||
          if (isRE(pattern)) {
 | 
					 | 
				
			||||||
            return toRegExp(pattern).toAutomaton().getNumberOfTransitions();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          } else if (pattern.endsWith("/*")) {
 | 
					 | 
				
			||||||
            return pattern.length();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
            return pattern.length();
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Sorts all given rights into a map, ordered by descending length of
 | 
					 | 
				
			||||||
   * ref pattern.
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * For example, if given the following rights in argument:
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * ["refs/heads/master", group1, -1, +1],
 | 
					 | 
				
			||||||
   * ["refs/heads/master", group2, -2, +2],
 | 
					 | 
				
			||||||
   * ["refs/heads/*", group3, -1, +1]
 | 
					 | 
				
			||||||
   * ["refs/heads/stable", group2, -1, +1]
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * Then the following map is returned:
 | 
					 | 
				
			||||||
   * "refs/heads/master" => {
 | 
					 | 
				
			||||||
   *      ["refs/heads/master", group1, -1, +1],
 | 
					 | 
				
			||||||
   *      ["refs/heads/master", group2, -2, +2]
 | 
					 | 
				
			||||||
   *  }
 | 
					 | 
				
			||||||
   * "refs/heads/stable" => {["refs/heads/stable", group2, -1, +1]}
 | 
					 | 
				
			||||||
   * "refs/heads/*" => {["refs/heads/*", group3, -1, +1]}
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @param actionRights
 | 
					 | 
				
			||||||
   * @return A sorted map keyed off the ref pattern of all rights.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  private SortedMap<String, RefRightsForPattern> sortedRightsByPattern(
 | 
					 | 
				
			||||||
      List<RefRight> actionRights) {
 | 
					 | 
				
			||||||
    SortedMap<String, RefRightsForPattern> rights =
 | 
					 | 
				
			||||||
      new TreeMap<String, RefRightsForPattern>(BY_MOST_SPECIFIC_SORT);
 | 
					 | 
				
			||||||
    for (RefRight actionRight : actionRights) {
 | 
					 | 
				
			||||||
      RefRightsForPattern patternRights =
 | 
					 | 
				
			||||||
        rights.get(actionRight.getRefPattern());
 | 
					 | 
				
			||||||
      if (patternRights == null) {
 | 
					 | 
				
			||||||
        patternRights = new RefRightsForPattern();
 | 
					 | 
				
			||||||
        rights.put(actionRight.getRefPattern(), patternRights);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      patternRights.addRight(actionRight);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return rights;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private List<RefRight> getAllRights(ApprovalCategory.Id actionId) {
 | 
					 | 
				
			||||||
    final List<RefRight> allRefRights = filter(getProjectState().getAllRights(actionId, true));
 | 
					 | 
				
			||||||
    return resolveOwnerGroups(allRefRights);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Returns all applicable rights for a given approval category.
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * Applicable rights are defined as the list of {@code RefRight}s which match
 | 
					 | 
				
			||||||
   * the ref for which this object was created, stopping the ref wildcard
 | 
					 | 
				
			||||||
   * matching when an exclusive ref right was encountered, for the given
 | 
					 | 
				
			||||||
   * approval category.
 | 
					 | 
				
			||||||
   * @param id The {@link ApprovalCategory.Id}.
 | 
					 | 
				
			||||||
   * @return All applicable rights.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  public List<RefRight> getApplicableRights(final ApprovalCategory.Id id) {
 | 
					 | 
				
			||||||
    List<RefRight> l = new ArrayList<RefRight>();
 | 
					 | 
				
			||||||
    l.addAll(getAllRights(id));
 | 
					 | 
				
			||||||
    SortedMap<String, RefRightsForPattern> perPatternRights =
 | 
					 | 
				
			||||||
      sortedRightsByPattern(l);
 | 
					 | 
				
			||||||
    List<RefRight> applicable = new ArrayList<RefRight>();
 | 
					 | 
				
			||||||
    for (RefRightsForPattern patternRights : perPatternRights.values()) {
 | 
					 | 
				
			||||||
      applicable.addAll(patternRights.getRights());
 | 
					 | 
				
			||||||
      if (patternRights.containsExclusive()) {
 | 
					 | 
				
			||||||
        break;
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return Collections.unmodifiableList(applicable);
 | 
					    return permissions;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  private boolean appliesToRef(AccessSection section) {
 | 
				
			||||||
   * Resolves all refRights which assign privileges to the 'Project Owners'
 | 
					    String refPattern = section.getRefPattern();
 | 
				
			||||||
   * group. All other refRights stay unchanged.
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @param refRights refRights to be resolved
 | 
					 | 
				
			||||||
   * @return the resolved refRights
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  private List<RefRight> resolveOwnerGroups(final List<RefRight> refRights) {
 | 
					 | 
				
			||||||
    final List<RefRight> resolvedRefRights =
 | 
					 | 
				
			||||||
        new ArrayList<RefRight>(refRights.size());
 | 
					 | 
				
			||||||
    for (final RefRight refRight : refRights) {
 | 
					 | 
				
			||||||
      resolvedRefRights.addAll(resolveOwnerGroups(refRight));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return resolvedRefRights;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Checks if the given refRight assigns privileges to the 'Project Owners'
 | 
					 | 
				
			||||||
   * group.
 | 
					 | 
				
			||||||
   * If yes, resolves the 'Project Owners' group to the concrete groups that
 | 
					 | 
				
			||||||
   * own the project and creates new refRights for the concrete owner groups
 | 
					 | 
				
			||||||
   * which are returned.
 | 
					 | 
				
			||||||
   * If no, the given refRight is returned unchanged.
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @param refRight refRight
 | 
					 | 
				
			||||||
   * @return the resolved refRights
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  private Set<RefRight> resolveOwnerGroups(final RefRight refRight) {
 | 
					 | 
				
			||||||
    final Set<RefRight> resolvedRefRights = new HashSet<RefRight>();
 | 
					 | 
				
			||||||
    if (AccountGroup.PROJECT_OWNERS.equals(refRight.getAccountGroupUUID())) {
 | 
					 | 
				
			||||||
      for (final AccountGroup.UUID ownerGroup : getProjectState().getAllOwners()) {
 | 
					 | 
				
			||||||
        if (!AccountGroup.PROJECT_OWNERS.equals(ownerGroup)) {
 | 
					 | 
				
			||||||
          resolvedRefRights.add(new RefRight(refRight, ownerGroup));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      resolvedRefRights.add(refRight);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return resolvedRefRights;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private List<RefRight> filter(Collection<RefRight> all) {
 | 
					 | 
				
			||||||
    List<RefRight> mine = new ArrayList<RefRight>(all.size());
 | 
					 | 
				
			||||||
    for (RefRight right : all) {
 | 
					 | 
				
			||||||
      if (matches(right.getRefPattern())) {
 | 
					 | 
				
			||||||
        mine.add(right);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return mine;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private ProjectState getProjectState() {
 | 
					 | 
				
			||||||
    return projectControl.getProjectState();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private boolean matches(String refPattern) {
 | 
					 | 
				
			||||||
    if (isTemplate(refPattern)) {
 | 
					    if (isTemplate(refPattern)) {
 | 
				
			||||||
      ParamertizedString template = new ParamertizedString(refPattern);
 | 
					      ParamertizedString template = new ParamertizedString(refPattern);
 | 
				
			||||||
      HashMap<String, String> p = new HashMap<String, String>();
 | 
					      HashMap<String, String> p = new HashMap<String, String>();
 | 
				
			||||||
@@ -596,6 +389,18 @@ public class RefControl {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private boolean matchGroup(AccountGroup.UUID uuid) {
 | 
				
			||||||
 | 
					    Set<AccountGroup.UUID> userGroups = getCurrentUser().getEffectiveGroups();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (AccountGroup.PROJECT_OWNERS.equals(uuid)) {
 | 
				
			||||||
 | 
					      ProjectState state = projectControl.getProjectState();
 | 
				
			||||||
 | 
					      return CollectionsUtil.isAnyIncludedIn(state.getAllOwners(), userGroups);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      return userGroups.contains(uuid);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static boolean isTemplate(String refPattern) {
 | 
					  private static boolean isTemplate(String refPattern) {
 | 
				
			||||||
    return 0 <= refPattern.indexOf("${");
 | 
					    return 0 <= refPattern.indexOf("${");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -608,7 +413,7 @@ public class RefControl {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static boolean isRE(String refPattern) {
 | 
					  private static boolean isRE(String refPattern) {
 | 
				
			||||||
    return refPattern.startsWith(RefRight.REGEX_PREFIX);
 | 
					    return refPattern.startsWith(AccessSection.REGEX_PREFIX);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public static String shortestExample(String pattern) {
 | 
					  public static String shortestExample(String pattern) {
 | 
				
			||||||
@@ -627,4 +432,143 @@ public class RefControl {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    return new RegExp(refPattern, RegExp.NONE);
 | 
					    return new RegExp(refPattern, RegExp.NONE);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Tracks whether or not a permission has been overridden. */
 | 
				
			||||||
 | 
					  private static class SeenRule {
 | 
				
			||||||
 | 
					    final String refPattern;
 | 
				
			||||||
 | 
					    final String permissionName;
 | 
				
			||||||
 | 
					    final AccountGroup.UUID group;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    SeenRule(AccessSection section, Permission permission, PermissionRule rule) {
 | 
				
			||||||
 | 
					      refPattern = section.getRefPattern();
 | 
				
			||||||
 | 
					      permissionName = permission.getName();
 | 
				
			||||||
 | 
					      group = rule.getGroup().getUUID();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public int hashCode() {
 | 
				
			||||||
 | 
					      int hc = refPattern.hashCode();
 | 
				
			||||||
 | 
					      hc = hc * 31 + permissionName.hashCode();
 | 
				
			||||||
 | 
					      if (group != null) {
 | 
				
			||||||
 | 
					        hc = hc * 31 + group.hashCode();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return hc;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public boolean equals(Object other) {
 | 
				
			||||||
 | 
					      if (other instanceof SeenRule) {
 | 
				
			||||||
 | 
					        SeenRule a = this;
 | 
				
			||||||
 | 
					        SeenRule b = (SeenRule) other;
 | 
				
			||||||
 | 
					        return a.refPattern.equals(b.refPattern) //
 | 
				
			||||||
 | 
					            && a.permissionName.equals(b.permissionName) //
 | 
				
			||||||
 | 
					            && eq(a.group, b.group);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean eq(AccountGroup.UUID a, AccountGroup.UUID b) {
 | 
				
			||||||
 | 
					      return a != null && b != null && a.equals(b);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					   * Order the Ref Pattern by the most specific. This sort is done by:
 | 
				
			||||||
 | 
					   * <ul>
 | 
				
			||||||
 | 
					   * <li>1 - The minor value of Levenshtein string distance between the branch
 | 
				
			||||||
 | 
					   * name and the regex string shortest example. A shorter distance is a more
 | 
				
			||||||
 | 
					   * specific match.
 | 
				
			||||||
 | 
					   * <li>2 - Finites first, infinities after.
 | 
				
			||||||
 | 
					   * <li>3 - Number of transitions.
 | 
				
			||||||
 | 
					   * <li>4 - Length of the expression text.
 | 
				
			||||||
 | 
					   * </ul>
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * Levenshtein distance is a measure of the similarity between two strings.
 | 
				
			||||||
 | 
					   * The distance is the number of deletions, insertions, or substitutions
 | 
				
			||||||
 | 
					   * required to transform one string into another.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * For example, if given refs/heads/m* and refs/heads/*, the distances are 5
 | 
				
			||||||
 | 
					   * and 6. It means that refs/heads/m* is more specific because it's closer to
 | 
				
			||||||
 | 
					   * refs/heads/master than refs/heads/*.
 | 
				
			||||||
 | 
					   *
 | 
				
			||||||
 | 
					   * Another example could be refs/heads/* and refs/heads/[a-zA-Z]*, the
 | 
				
			||||||
 | 
					   * distances are both 6. Both are infinite, but refs/heads/[a-zA-Z]* has more
 | 
				
			||||||
 | 
					   * transitions, which after all turns it more specific.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  private static final class MostSpecificComparator implements
 | 
				
			||||||
 | 
					      Comparator<AccessSection> {
 | 
				
			||||||
 | 
					    private final String refName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MostSpecificComparator(String refName) {
 | 
				
			||||||
 | 
					      this.refName = refName;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public int compare(AccessSection a, AccessSection b) {
 | 
				
			||||||
 | 
					      return compare(a.getRefPattern(), b.getRefPattern());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private int compare(final String pattern1, final String pattern2) {
 | 
				
			||||||
 | 
					      int cmp = distance(pattern1) - distance(pattern2);
 | 
				
			||||||
 | 
					      if (cmp == 0) {
 | 
				
			||||||
 | 
					        boolean p1_finite = finite(pattern1);
 | 
				
			||||||
 | 
					        boolean p2_finite = finite(pattern2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (p1_finite && !p2_finite) {
 | 
				
			||||||
 | 
					          cmp = -1;
 | 
				
			||||||
 | 
					        } else if (!p1_finite && p2_finite) {
 | 
				
			||||||
 | 
					          cmp = 1;
 | 
				
			||||||
 | 
					        } else /* if (f1 == f2) */{
 | 
				
			||||||
 | 
					          cmp = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (cmp == 0) {
 | 
				
			||||||
 | 
					        cmp = transitions(pattern1) - transitions(pattern2);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if (cmp == 0) {
 | 
				
			||||||
 | 
					        cmp = pattern2.length() - pattern1.length();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return cmp;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private int distance(String pattern) {
 | 
				
			||||||
 | 
					      String example;
 | 
				
			||||||
 | 
					      if (isRE(pattern)) {
 | 
				
			||||||
 | 
					        example = shortestExample(pattern);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } else if (pattern.endsWith("/*")) {
 | 
				
			||||||
 | 
					        example = pattern.substring(0, pattern.length() - 1) + '1';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } else if (pattern.equals(refName)) {
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return Math.max(pattern.length(), refName.length());
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return StringUtils.getLevenshteinDistance(example, refName);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean finite(String pattern) {
 | 
				
			||||||
 | 
					      if (isRE(pattern)) {
 | 
				
			||||||
 | 
					        return toRegExp(pattern).toAutomaton().isFinite();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } else if (pattern.endsWith("/*")) {
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return true;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private int transitions(String pattern) {
 | 
				
			||||||
 | 
					      if (isRE(pattern)) {
 | 
				
			||||||
 | 
					        return toRegExp(pattern).toAutomaton().getNumberOfTransitions();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } else if (pattern.endsWith("/*")) {
 | 
				
			||||||
 | 
					        return pattern.length();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        return pattern.length();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,6 +16,7 @@ package com.google.gerrit.server.query.change;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.google.gerrit.common.data.ApprovalType;
 | 
					import com.google.gerrit.common.data.ApprovalType;
 | 
				
			||||||
import com.google.gerrit.common.data.ApprovalTypes;
 | 
					import com.google.gerrit.common.data.ApprovalTypes;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.Permission;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
					import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
				
			||||||
import com.google.gerrit.reviewdb.PatchSetApproval;
 | 
					import com.google.gerrit.reviewdb.PatchSetApproval;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ReviewDb;
 | 
					import com.google.gerrit.reviewdb.ReviewDb;
 | 
				
			||||||
@@ -33,48 +34,54 @@ class LabelPredicate extends OperatorPredicate<ChangeData> {
 | 
				
			|||||||
  private static enum Test {
 | 
					  private static enum Test {
 | 
				
			||||||
    EQ {
 | 
					    EQ {
 | 
				
			||||||
      @Override
 | 
					      @Override
 | 
				
			||||||
      public boolean match(short psValue, short expValue) {
 | 
					      public boolean match(int psValue, int expValue) {
 | 
				
			||||||
        return psValue == expValue;
 | 
					        return psValue == expValue;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    GT_EQ {
 | 
					    GT_EQ {
 | 
				
			||||||
      @Override
 | 
					      @Override
 | 
				
			||||||
      public boolean match(short psValue, short expValue) {
 | 
					      public boolean match(int psValue, int expValue) {
 | 
				
			||||||
        return psValue >= expValue;
 | 
					        return psValue >= expValue;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    LT_EQ {
 | 
					    LT_EQ {
 | 
				
			||||||
      @Override
 | 
					      @Override
 | 
				
			||||||
      public boolean match(short psValue, short expValue) {
 | 
					      public boolean match(int psValue, int expValue) {
 | 
				
			||||||
        return psValue <= expValue;
 | 
					        return psValue <= expValue;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    abstract boolean match(short psValue, short expValue);
 | 
					    abstract boolean match(int psValue, int expValue);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static ApprovalCategory.Id category(ApprovalTypes types, String toFind) {
 | 
					  private static ApprovalCategory category(ApprovalTypes types, String toFind) {
 | 
				
			||||||
    if (types.getApprovalType(new ApprovalCategory.Id(toFind)) != null) {
 | 
					    if (types.byLabel(toFind) != null) {
 | 
				
			||||||
      return new ApprovalCategory.Id(toFind);
 | 
					      return types.byLabel(toFind).getCategory();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (types.byId(new ApprovalCategory.Id(toFind)) != null) {
 | 
				
			||||||
 | 
					      return types.byId(new ApprovalCategory.Id(toFind)).getCategory();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (ApprovalType at : types.getApprovalTypes()) {
 | 
					    for (ApprovalType at : types.getApprovalTypes()) {
 | 
				
			||||||
      String name = at.getCategory().getName();
 | 
					      ApprovalCategory category = at.getCategory();
 | 
				
			||||||
      if (toFind.equalsIgnoreCase(name)) {
 | 
					 | 
				
			||||||
        return at.getCategory().getId();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
      } else if (toFind.equalsIgnoreCase(name.replace(" ", ""))) {
 | 
					      if (toFind.equalsIgnoreCase(category.getName())) {
 | 
				
			||||||
        return at.getCategory().getId();
 | 
					        return category;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } else if (toFind.equalsIgnoreCase(category.getName().replace(" ", ""))) {
 | 
				
			||||||
 | 
					        return category;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (ApprovalType at : types.getApprovalTypes()) {
 | 
					    for (ApprovalType at : types.getApprovalTypes()) {
 | 
				
			||||||
      if (toFind.equalsIgnoreCase(at.getCategory().getAbbreviatedName())) {
 | 
					      ApprovalCategory category = at.getCategory();
 | 
				
			||||||
        return at.getCategory().getId();
 | 
					      if (toFind.equalsIgnoreCase(category.getAbbreviatedName())) {
 | 
				
			||||||
 | 
					        return category;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return new ApprovalCategory.Id(toFind);
 | 
					    return new ApprovalCategory(new ApprovalCategory.Id(toFind), toFind);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static Test op(String op) {
 | 
					  private static Test op(String op) {
 | 
				
			||||||
@@ -92,19 +99,20 @@ class LabelPredicate extends OperatorPredicate<ChangeData> {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static short value(String value) {
 | 
					  private static int value(String value) {
 | 
				
			||||||
    if (value.startsWith("+")) {
 | 
					    if (value.startsWith("+")) {
 | 
				
			||||||
      value = value.substring(1);
 | 
					      value = value.substring(1);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    return Short.parseShort(value);
 | 
					    return Integer.parseInt(value);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private final ChangeControl.GenericFactory ccFactory;
 | 
					  private final ChangeControl.GenericFactory ccFactory;
 | 
				
			||||||
  private final IdentifiedUser.GenericFactory userFactory;
 | 
					  private final IdentifiedUser.GenericFactory userFactory;
 | 
				
			||||||
  private final Provider<ReviewDb> dbProvider;
 | 
					  private final Provider<ReviewDb> dbProvider;
 | 
				
			||||||
  private final Test test;
 | 
					  private final Test test;
 | 
				
			||||||
  private final ApprovalCategory.Id category;
 | 
					  private final ApprovalCategory category;
 | 
				
			||||||
  private final short expVal;
 | 
					  private final String permissionName;
 | 
				
			||||||
 | 
					  private final int expVal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  LabelPredicate(ChangeControl.GenericFactory ccFactory,
 | 
					  LabelPredicate(ChangeControl.GenericFactory ccFactory,
 | 
				
			||||||
      IdentifiedUser.GenericFactory userFactory, Provider<ReviewDb> dbProvider,
 | 
					      IdentifiedUser.GenericFactory userFactory, Provider<ReviewDb> dbProvider,
 | 
				
			||||||
@@ -131,13 +139,15 @@ class LabelPredicate extends OperatorPredicate<ChangeData> {
 | 
				
			|||||||
      test = Test.EQ;
 | 
					      test = Test.EQ;
 | 
				
			||||||
      expVal = 1;
 | 
					      expVal = 1;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    this.permissionName = Permission.forLabel(category.getLabelName());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  public boolean match(final ChangeData object) throws OrmException {
 | 
					  public boolean match(final ChangeData object) throws OrmException {
 | 
				
			||||||
    for (PatchSetApproval p : object.currentApprovals(dbProvider)) {
 | 
					    for (PatchSetApproval p : object.currentApprovals(dbProvider)) {
 | 
				
			||||||
      if (p.getCategoryId().equals(category)) {
 | 
					      if (p.getCategoryId().equals(category)) {
 | 
				
			||||||
        short psVal = p.getValue();
 | 
					        int psVal = p.getValue();
 | 
				
			||||||
        if (test.match(psVal, expVal)) {
 | 
					        if (test.match(psVal, expVal)) {
 | 
				
			||||||
          // Double check the value is still permitted for the user.
 | 
					          // Double check the value is still permitted for the user.
 | 
				
			||||||
          //
 | 
					          //
 | 
				
			||||||
@@ -149,7 +159,7 @@ class LabelPredicate extends OperatorPredicate<ChangeData> {
 | 
				
			|||||||
              //
 | 
					              //
 | 
				
			||||||
              continue;
 | 
					              continue;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            psVal = cc.normalize(category, psVal);
 | 
					            psVal = cc.getRange(permissionName).squash(psVal);
 | 
				
			||||||
          } catch (NoSuchChangeException e) {
 | 
					          } catch (NoSuchChangeException e) {
 | 
				
			||||||
            // The project has disappeared.
 | 
					            // The project has disappeared.
 | 
				
			||||||
            //
 | 
					            //
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,13 +14,16 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.google.gerrit.server.schema;
 | 
					package com.google.gerrit.server.schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.common.Version;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.AccessSection;
 | 
				
			||||||
 | 
					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.AccountGroup;
 | 
				
			||||||
import com.google.gerrit.reviewdb.AccountGroupName;
 | 
					import com.google.gerrit.reviewdb.AccountGroupName;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
					import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
 | 
					import com.google.gerrit.reviewdb.ApprovalCategoryValue;
 | 
				
			||||||
import com.google.gerrit.reviewdb.CurrentSchemaVersion;
 | 
					import com.google.gerrit.reviewdb.CurrentSchemaVersion;
 | 
				
			||||||
import com.google.gerrit.reviewdb.Project;
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
import com.google.gerrit.reviewdb.RefRight;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.ReviewDb;
 | 
					import com.google.gerrit.reviewdb.ReviewDb;
 | 
				
			||||||
import com.google.gerrit.reviewdb.SystemConfig;
 | 
					import com.google.gerrit.reviewdb.SystemConfig;
 | 
				
			||||||
import com.google.gerrit.server.GerritPersonIdent;
 | 
					import com.google.gerrit.server.GerritPersonIdent;
 | 
				
			||||||
@@ -31,8 +34,6 @@ import com.google.gerrit.server.git.GitRepositoryManager;
 | 
				
			|||||||
import com.google.gerrit.server.git.MetaDataUpdate;
 | 
					import com.google.gerrit.server.git.MetaDataUpdate;
 | 
				
			||||||
import com.google.gerrit.server.git.NoReplication;
 | 
					import com.google.gerrit.server.git.NoReplication;
 | 
				
			||||||
import com.google.gerrit.server.git.ProjectConfig;
 | 
					import com.google.gerrit.server.git.ProjectConfig;
 | 
				
			||||||
import com.google.gerrit.server.workflow.NoOpFunction;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.workflow.SubmitFunction;
 | 
					 | 
				
			||||||
import com.google.gwtjsonrpc.server.SignedToken;
 | 
					import com.google.gwtjsonrpc.server.SignedToken;
 | 
				
			||||||
import com.google.gwtorm.client.OrmException;
 | 
					import com.google.gwtorm.client.OrmException;
 | 
				
			||||||
import com.google.gwtorm.jdbc.JdbcExecutor;
 | 
					import com.google.gwtorm.jdbc.JdbcExecutor;
 | 
				
			||||||
@@ -69,6 +70,11 @@ public class SchemaCreator {
 | 
				
			|||||||
  private final ScriptRunner index_postgres;
 | 
					  private final ScriptRunner index_postgres;
 | 
				
			||||||
  private final ScriptRunner mysql_nextval;
 | 
					  private final ScriptRunner mysql_nextval;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private AccountGroup admin;
 | 
				
			||||||
 | 
					  private AccountGroup anonymous;
 | 
				
			||||||
 | 
					  private AccountGroup registered;
 | 
				
			||||||
 | 
					  private AccountGroup owners;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  public SchemaCreator(final SitePaths site,
 | 
					  public SchemaCreator(final SitePaths site,
 | 
				
			||||||
      @Current final SchemaVersion version, final GitRepositoryManager mgr,
 | 
					      @Current final SchemaVersion version, final GitRepositoryManager mgr,
 | 
				
			||||||
@@ -103,14 +109,8 @@ public class SchemaCreator {
 | 
				
			|||||||
    db.schemaVersion().insert(Collections.singleton(sVer));
 | 
					    db.schemaVersion().insert(Collections.singleton(sVer));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final SystemConfig sConfig = initSystemConfig(db);
 | 
					    final SystemConfig sConfig = initSystemConfig(db);
 | 
				
			||||||
    initOwnerCategory(db);
 | 
					 | 
				
			||||||
    initReadCategory(db, sConfig);
 | 
					 | 
				
			||||||
    initVerifiedCategory(db);
 | 
					    initVerifiedCategory(db);
 | 
				
			||||||
    initCodeReviewCategory(db, sConfig);
 | 
					    initCodeReviewCategory(db, sConfig);
 | 
				
			||||||
    initSubmitCategory(db);
 | 
					 | 
				
			||||||
    initPushTagCategory(db);
 | 
					 | 
				
			||||||
    initPushUpdateBranchCategory(db);
 | 
					 | 
				
			||||||
    initForgeIdentityCategory(db, sConfig);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (mgr != null) {
 | 
					    if (mgr != null) {
 | 
				
			||||||
      // TODO This should never be null when initializing a site.
 | 
					      // TODO This should never be null when initializing a site.
 | 
				
			||||||
@@ -145,14 +145,14 @@ public class SchemaCreator {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private SystemConfig initSystemConfig(final ReviewDb c) throws OrmException {
 | 
					  private SystemConfig initSystemConfig(final ReviewDb c) throws OrmException {
 | 
				
			||||||
    final AccountGroup admin = newGroup(c, "Administrators", null);
 | 
					    admin = newGroup(c, "Administrators", null);
 | 
				
			||||||
    admin.setDescription("Gerrit Site Administrators");
 | 
					    admin.setDescription("Gerrit Site Administrators");
 | 
				
			||||||
    admin.setType(AccountGroup.Type.INTERNAL);
 | 
					    admin.setType(AccountGroup.Type.INTERNAL);
 | 
				
			||||||
    c.accountGroups().insert(Collections.singleton(admin));
 | 
					    c.accountGroups().insert(Collections.singleton(admin));
 | 
				
			||||||
    c.accountGroupNames().insert(
 | 
					    c.accountGroupNames().insert(
 | 
				
			||||||
        Collections.singleton(new AccountGroupName(admin)));
 | 
					        Collections.singleton(new AccountGroupName(admin)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final AccountGroup anonymous =
 | 
					    anonymous =
 | 
				
			||||||
        newGroup(c, "Anonymous Users", AccountGroup.ANONYMOUS_USERS);
 | 
					        newGroup(c, "Anonymous Users", AccountGroup.ANONYMOUS_USERS);
 | 
				
			||||||
    anonymous.setDescription("Any user, signed-in or not");
 | 
					    anonymous.setDescription("Any user, signed-in or not");
 | 
				
			||||||
    anonymous.setOwnerGroupId(admin.getId());
 | 
					    anonymous.setOwnerGroupId(admin.getId());
 | 
				
			||||||
@@ -161,7 +161,7 @@ public class SchemaCreator {
 | 
				
			|||||||
    c.accountGroupNames().insert(
 | 
					    c.accountGroupNames().insert(
 | 
				
			||||||
        Collections.singleton(new AccountGroupName(anonymous)));
 | 
					        Collections.singleton(new AccountGroupName(anonymous)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final AccountGroup registered =
 | 
					    registered =
 | 
				
			||||||
        newGroup(c, "Registered Users", AccountGroup.REGISTERED_USERS);
 | 
					        newGroup(c, "Registered Users", AccountGroup.REGISTERED_USERS);
 | 
				
			||||||
    registered.setDescription("Any signed-in user");
 | 
					    registered.setDescription("Any signed-in user");
 | 
				
			||||||
    registered.setOwnerGroupId(admin.getId());
 | 
					    registered.setOwnerGroupId(admin.getId());
 | 
				
			||||||
@@ -178,8 +178,7 @@ public class SchemaCreator {
 | 
				
			|||||||
    c.accountGroupNames().insert(
 | 
					    c.accountGroupNames().insert(
 | 
				
			||||||
        Collections.singleton(new AccountGroupName(batchUsers)));
 | 
					        Collections.singleton(new AccountGroupName(batchUsers)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    final AccountGroup owners =
 | 
					    owners = newGroup(c, "Project Owners", AccountGroup.PROJECT_OWNERS);
 | 
				
			||||||
        newGroup(c, "Project Owners", AccountGroup.PROJECT_OWNERS);
 | 
					 | 
				
			||||||
    owners.setDescription("Any owner of the project");
 | 
					    owners.setDescription("Any owner of the project");
 | 
				
			||||||
    owners.setOwnerGroupId(admin.getId());
 | 
					    owners.setOwnerGroupId(admin.getId());
 | 
				
			||||||
    owners.setType(AccountGroup.Type.SYSTEM);
 | 
					    owners.setType(AccountGroup.Type.SYSTEM);
 | 
				
			||||||
@@ -236,7 +235,29 @@ public class SchemaCreator {
 | 
				
			|||||||
      p.setDescription("Rights inherited by all other projects");
 | 
					      p.setDescription("Rights inherited by all other projects");
 | 
				
			||||||
      p.setUseContributorAgreements(false);
 | 
					      p.setUseContributorAgreements(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      md.setMessage("Created project\n");
 | 
					      AccessSection all = config.getAccessSection(AccessSection.ALL, true);
 | 
				
			||||||
 | 
					      AccessSection heads = config.getAccessSection(AccessSection.HEADS, true);
 | 
				
			||||||
 | 
					      AccessSection meta = config.getAccessSection(GitRepositoryManager.REF_CONFIG, true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      PermissionRule review = rule(config, registered);
 | 
				
			||||||
 | 
					      review.setRange(-1, 1);
 | 
				
			||||||
 | 
					      heads.getPermission(Permission.LABEL + "Code-Review", true).add(review);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      all.getPermission(Permission.READ, true) //
 | 
				
			||||||
 | 
					          .add(rule(config, admin));
 | 
				
			||||||
 | 
					      all.getPermission(Permission.READ, true) //
 | 
				
			||||||
 | 
					          .add(rule(config, anonymous));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      config.getAccessSection("refs/for/" + AccessSection.ALL, true) //
 | 
				
			||||||
 | 
					          .getPermission(Permission.PUSH, true) //
 | 
				
			||||||
 | 
					          .add(rule(config, registered));
 | 
				
			||||||
 | 
					      all.getPermission(Permission.FORGE_AUTHOR, true) //
 | 
				
			||||||
 | 
					          .add(rule(config, registered));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      meta.getPermission(Permission.READ, true) //
 | 
				
			||||||
 | 
					          .add(rule(config, owners));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      md.setMessage("Initialized Gerrit Code Review " + Version.getVersion());
 | 
				
			||||||
      if (!config.commit(md)) {
 | 
					      if (!config.commit(md)) {
 | 
				
			||||||
        throw new IOException("Cannot create " + DEFAULT_WILD_NAME.get());
 | 
					        throw new IOException("Cannot create " + DEFAULT_WILD_NAME.get());
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
@@ -245,6 +266,10 @@ public class SchemaCreator {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private PermissionRule rule(ProjectConfig config, AccountGroup group) {
 | 
				
			||||||
 | 
					    return new PermissionRule(config.resolve(group));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private void initVerifiedCategory(final ReviewDb c) throws OrmException {
 | 
					  private void initVerifiedCategory(final ReviewDb c) throws OrmException {
 | 
				
			||||||
    final ApprovalCategory cat;
 | 
					    final ApprovalCategory cat;
 | 
				
			||||||
    final ArrayList<ApprovalCategoryValue> vals;
 | 
					    final ArrayList<ApprovalCategoryValue> vals;
 | 
				
			||||||
@@ -277,143 +302,6 @@ public class SchemaCreator {
 | 
				
			|||||||
    vals.add(value(cat, -2, "Do not submit"));
 | 
					    vals.add(value(cat, -2, "Do not submit"));
 | 
				
			||||||
    c.approvalCategories().insert(Collections.singleton(cat));
 | 
					    c.approvalCategories().insert(Collections.singleton(cat));
 | 
				
			||||||
    c.approvalCategoryValues().insert(vals);
 | 
					    c.approvalCategoryValues().insert(vals);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    final RefRight approve =
 | 
					 | 
				
			||||||
        new RefRight(new RefRight.Key(DEFAULT_WILD_NAME,
 | 
					 | 
				
			||||||
            new RefRight.RefPattern("refs/heads/*"), cat.getId(),
 | 
					 | 
				
			||||||
            sConfig.registeredGroupId));
 | 
					 | 
				
			||||||
    approve.setMaxValue((short) 1);
 | 
					 | 
				
			||||||
    approve.setMinValue((short) -1);
 | 
					 | 
				
			||||||
    c.refRights().insert(Collections.singleton(approve));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private void initOwnerCategory(final ReviewDb c) throws OrmException {
 | 
					 | 
				
			||||||
    final ApprovalCategory cat;
 | 
					 | 
				
			||||||
    final ArrayList<ApprovalCategoryValue> vals;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cat = new ApprovalCategory(ApprovalCategory.OWN, "Owner");
 | 
					 | 
				
			||||||
    cat.setPosition((short) -1);
 | 
					 | 
				
			||||||
    cat.setFunctionName(NoOpFunction.NAME);
 | 
					 | 
				
			||||||
    vals = new ArrayList<ApprovalCategoryValue>();
 | 
					 | 
				
			||||||
    vals.add(value(cat, 1, "Administer All Settings"));
 | 
					 | 
				
			||||||
    c.approvalCategories().insert(Collections.singleton(cat));
 | 
					 | 
				
			||||||
    c.approvalCategoryValues().insert(vals);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private void initReadCategory(final ReviewDb c, final SystemConfig sConfig)
 | 
					 | 
				
			||||||
      throws OrmException {
 | 
					 | 
				
			||||||
    final ApprovalCategory cat;
 | 
					 | 
				
			||||||
    final ArrayList<ApprovalCategoryValue> vals;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cat = new ApprovalCategory(ApprovalCategory.READ, "Read Access");
 | 
					 | 
				
			||||||
    cat.setPosition((short) -1);
 | 
					 | 
				
			||||||
    cat.setFunctionName(NoOpFunction.NAME);
 | 
					 | 
				
			||||||
    vals = new ArrayList<ApprovalCategoryValue>();
 | 
					 | 
				
			||||||
    vals.add(value(cat, 3, "Upload merges permission"));
 | 
					 | 
				
			||||||
    vals.add(value(cat, 2, "Upload permission"));
 | 
					 | 
				
			||||||
    vals.add(value(cat, 1, "Read access"));
 | 
					 | 
				
			||||||
    vals.add(value(cat, -1, "No access"));
 | 
					 | 
				
			||||||
    c.approvalCategories().insert(Collections.singleton(cat));
 | 
					 | 
				
			||||||
    c.approvalCategoryValues().insert(vals);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    final RefRight.RefPattern pattern = new RefRight.RefPattern(RefRight.ALL);
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      final RefRight read =
 | 
					 | 
				
			||||||
          new RefRight(new RefRight.Key(DEFAULT_WILD_NAME, pattern,
 | 
					 | 
				
			||||||
              cat.getId(), sConfig.anonymousGroupId));
 | 
					 | 
				
			||||||
      read.setMaxValue((short) 1);
 | 
					 | 
				
			||||||
      read.setMinValue((short) 1);
 | 
					 | 
				
			||||||
      c.refRights().insert(Collections.singleton(read));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      final RefRight read =
 | 
					 | 
				
			||||||
          new RefRight(new RefRight.Key(DEFAULT_WILD_NAME, pattern,
 | 
					 | 
				
			||||||
              cat.getId(), sConfig.registeredGroupId));
 | 
					 | 
				
			||||||
      read.setMaxValue((short) 2);
 | 
					 | 
				
			||||||
      read.setMinValue((short) 1);
 | 
					 | 
				
			||||||
      c.refRights().insert(Collections.singleton(read));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
      final RefRight read =
 | 
					 | 
				
			||||||
          new RefRight(new RefRight.Key(DEFAULT_WILD_NAME, pattern,
 | 
					 | 
				
			||||||
              cat.getId(), sConfig.adminGroupId));
 | 
					 | 
				
			||||||
      read.setMaxValue((short) 1);
 | 
					 | 
				
			||||||
      read.setMinValue((short) 1);
 | 
					 | 
				
			||||||
      c.refRights().insert(Collections.singleton(read));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private void initSubmitCategory(final ReviewDb c) throws OrmException {
 | 
					 | 
				
			||||||
    final ApprovalCategory cat;
 | 
					 | 
				
			||||||
    final ArrayList<ApprovalCategoryValue> vals;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cat = new ApprovalCategory(ApprovalCategory.SUBMIT, "Submit");
 | 
					 | 
				
			||||||
    cat.setPosition((short) -1);
 | 
					 | 
				
			||||||
    cat.setFunctionName(SubmitFunction.NAME);
 | 
					 | 
				
			||||||
    vals = new ArrayList<ApprovalCategoryValue>();
 | 
					 | 
				
			||||||
    vals.add(value(cat, 1, "Submit"));
 | 
					 | 
				
			||||||
    c.approvalCategories().insert(Collections.singleton(cat));
 | 
					 | 
				
			||||||
    c.approvalCategoryValues().insert(vals);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private void initPushTagCategory(final ReviewDb c) throws OrmException {
 | 
					 | 
				
			||||||
    final ApprovalCategory cat;
 | 
					 | 
				
			||||||
    final ArrayList<ApprovalCategoryValue> vals;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cat = new ApprovalCategory(ApprovalCategory.PUSH_TAG, "Push Tag");
 | 
					 | 
				
			||||||
    cat.setPosition((short) -1);
 | 
					 | 
				
			||||||
    cat.setFunctionName(NoOpFunction.NAME);
 | 
					 | 
				
			||||||
    vals = new ArrayList<ApprovalCategoryValue>();
 | 
					 | 
				
			||||||
    vals.add(value(cat, ApprovalCategory.PUSH_TAG_SIGNED, "Create Signed Tag"));
 | 
					 | 
				
			||||||
    vals.add(value(cat, ApprovalCategory.PUSH_TAG_ANNOTATED,
 | 
					 | 
				
			||||||
        "Create Annotated Tag"));
 | 
					 | 
				
			||||||
    c.approvalCategories().insert(Collections.singleton(cat));
 | 
					 | 
				
			||||||
    c.approvalCategoryValues().insert(vals);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private void initPushUpdateBranchCategory(final ReviewDb c)
 | 
					 | 
				
			||||||
      throws OrmException {
 | 
					 | 
				
			||||||
    final ApprovalCategory cat;
 | 
					 | 
				
			||||||
    final ArrayList<ApprovalCategoryValue> vals;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cat = new ApprovalCategory(ApprovalCategory.PUSH_HEAD, "Push Branch");
 | 
					 | 
				
			||||||
    cat.setPosition((short) -1);
 | 
					 | 
				
			||||||
    cat.setFunctionName(NoOpFunction.NAME);
 | 
					 | 
				
			||||||
    vals = new ArrayList<ApprovalCategoryValue>();
 | 
					 | 
				
			||||||
    vals.add(value(cat, ApprovalCategory.PUSH_HEAD_UPDATE, "Update Branch"));
 | 
					 | 
				
			||||||
    vals.add(value(cat, ApprovalCategory.PUSH_HEAD_CREATE, "Create Branch"));
 | 
					 | 
				
			||||||
    vals.add(value(cat, ApprovalCategory.PUSH_HEAD_REPLACE,
 | 
					 | 
				
			||||||
        "Force Push Branch; Delete Branch"));
 | 
					 | 
				
			||||||
    c.approvalCategories().insert(Collections.singleton(cat));
 | 
					 | 
				
			||||||
    c.approvalCategoryValues().insert(vals);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private void initForgeIdentityCategory(final ReviewDb c,
 | 
					 | 
				
			||||||
      final SystemConfig sConfig) throws OrmException {
 | 
					 | 
				
			||||||
    final ApprovalCategory cat;
 | 
					 | 
				
			||||||
    final ArrayList<ApprovalCategoryValue> values;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    cat =
 | 
					 | 
				
			||||||
        new ApprovalCategory(ApprovalCategory.FORGE_IDENTITY, "Forge Identity");
 | 
					 | 
				
			||||||
    cat.setPosition((short) -1);
 | 
					 | 
				
			||||||
    cat.setFunctionName(NoOpFunction.NAME);
 | 
					 | 
				
			||||||
    values = new ArrayList<ApprovalCategoryValue>();
 | 
					 | 
				
			||||||
    values.add(value(cat, ApprovalCategory.FORGE_AUTHOR,
 | 
					 | 
				
			||||||
        "Forge Author Identity"));
 | 
					 | 
				
			||||||
    values.add(value(cat, ApprovalCategory.FORGE_COMMITTER,
 | 
					 | 
				
			||||||
        "Forge Committer or Tagger Identity"));
 | 
					 | 
				
			||||||
    values.add(value(cat, ApprovalCategory.FORGE_SERVER,
 | 
					 | 
				
			||||||
        "Forge Gerrit Code Review Server Identity"));
 | 
					 | 
				
			||||||
    c.approvalCategories().insert(Collections.singleton(cat));
 | 
					 | 
				
			||||||
    c.approvalCategoryValues().insert(values);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    RefRight right =
 | 
					 | 
				
			||||||
        new RefRight(new RefRight.Key(sConfig.wildProjectName,
 | 
					 | 
				
			||||||
            new RefRight.RefPattern(RefRight.ALL),
 | 
					 | 
				
			||||||
            ApprovalCategory.FORGE_IDENTITY, sConfig.registeredGroupId));
 | 
					 | 
				
			||||||
    right.setMinValue(ApprovalCategory.FORGE_AUTHOR);
 | 
					 | 
				
			||||||
    right.setMaxValue(ApprovalCategory.FORGE_AUTHOR);
 | 
					 | 
				
			||||||
    c.refRights().insert(Collections.singleton(right));
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static ApprovalCategoryValue value(final ApprovalCategory cat,
 | 
					  private static ApprovalCategoryValue value(final ApprovalCategory cat,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,24 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.google.gerrit.server.schema;
 | 
					package com.google.gerrit.server.schema;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static com.google.gerrit.common.data.Permission.CREATE;
 | 
				
			||||||
 | 
					import static com.google.gerrit.common.data.Permission.FORGE_AUTHOR;
 | 
				
			||||||
 | 
					import static com.google.gerrit.common.data.Permission.FORGE_COMMITTER;
 | 
				
			||||||
 | 
					import static com.google.gerrit.common.data.Permission.FORGE_SERVER;
 | 
				
			||||||
 | 
					import static com.google.gerrit.common.data.Permission.LABEL;
 | 
				
			||||||
 | 
					import static com.google.gerrit.common.data.Permission.OWNER;
 | 
				
			||||||
 | 
					import static com.google.gerrit.common.data.Permission.PUSH;
 | 
				
			||||||
 | 
					import static com.google.gerrit.common.data.Permission.PUSH_MERGE;
 | 
				
			||||||
 | 
					import static com.google.gerrit.common.data.Permission.PUSH_TAG;
 | 
				
			||||||
 | 
					import static com.google.gerrit.common.data.Permission.READ;
 | 
				
			||||||
 | 
					import static com.google.gerrit.common.data.Permission.SUBMIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.AccountGroup;
 | 
				
			||||||
 | 
					import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
				
			||||||
import com.google.gerrit.reviewdb.Project;
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ReviewDb;
 | 
					import com.google.gerrit.reviewdb.ReviewDb;
 | 
				
			||||||
import com.google.gerrit.reviewdb.SystemConfig;
 | 
					import com.google.gerrit.reviewdb.SystemConfig;
 | 
				
			||||||
@@ -38,6 +55,7 @@ import java.io.IOException;
 | 
				
			|||||||
import java.sql.ResultSet;
 | 
					import java.sql.ResultSet;
 | 
				
			||||||
import java.sql.SQLException;
 | 
					import java.sql.SQLException;
 | 
				
			||||||
import java.sql.Statement;
 | 
					import java.sql.Statement;
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
import java.util.Collections;
 | 
					import java.util.Collections;
 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.List;
 | 
					import java.util.List;
 | 
				
			||||||
@@ -48,7 +66,19 @@ class Schema_53 extends SchemaVersion {
 | 
				
			|||||||
  private final PersonIdent serverUser;
 | 
					  private final PersonIdent serverUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private SystemConfig systemConfig;
 | 
					  private SystemConfig systemConfig;
 | 
				
			||||||
  private Map<AccountGroup.Id, AccountGroup> groupMap;
 | 
					  private Map<AccountGroup.Id, GroupReference> groupMap;
 | 
				
			||||||
 | 
					  private Map<ApprovalCategory.Id, ApprovalCategory> categoryMap;
 | 
				
			||||||
 | 
					  private GroupReference projectOwners;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private Map<Project.NameKey, Project.NameKey> parentsByProject;
 | 
				
			||||||
 | 
					  private Map<Project.NameKey, List<OldRefRight>> rightsByProject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private final String OLD_SUBMIT = "SUBM";
 | 
				
			||||||
 | 
					  private final String OLD_READ = "READ";
 | 
				
			||||||
 | 
					  private final String OLD_OWN = "OWN";
 | 
				
			||||||
 | 
					  private final String OLD_PUSH_TAG = "pTAG";
 | 
				
			||||||
 | 
					  private final String OLD_PUSH_HEAD = "pHD";
 | 
				
			||||||
 | 
					  private final String OLD_FORGE_IDENTITY = "FORG";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  Schema_53(Provider<Schema_52> prior, GitRepositoryManager mgr,
 | 
					  Schema_53(Provider<Schema_52> prior, GitRepositoryManager mgr,
 | 
				
			||||||
@@ -62,16 +92,33 @@ class Schema_53 extends SchemaVersion {
 | 
				
			|||||||
  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException,
 | 
					  protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException,
 | 
				
			||||||
      SQLException {
 | 
					      SQLException {
 | 
				
			||||||
    systemConfig = db.systemConfig().get(new SystemConfig.Key());
 | 
					    systemConfig = db.systemConfig().get(new SystemConfig.Key());
 | 
				
			||||||
 | 
					    categoryMap = db.approvalCategories().toMap(db.approvalCategories().all());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    assignGroupUUIDs(db);
 | 
					    assignGroupUUIDs(db);
 | 
				
			||||||
 | 
					    readOldRefRights(db);
 | 
				
			||||||
 | 
					    readProjectParents(db);
 | 
				
			||||||
    exportProjectConfig(db);
 | 
					    exportProjectConfig(db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    deleteActionCategories(db);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void deleteActionCategories(ReviewDb db) throws OrmException {
 | 
				
			||||||
 | 
					    List<ApprovalCategory> delete = new ArrayList<ApprovalCategory>();
 | 
				
			||||||
 | 
					    for (ApprovalCategory category : categoryMap.values()) {
 | 
				
			||||||
 | 
					      if (category.getPosition() < 0) {
 | 
				
			||||||
 | 
					        delete.add(category);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    db.approvalCategories().delete(delete);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private void assignGroupUUIDs(ReviewDb db) throws OrmException {
 | 
					  private void assignGroupUUIDs(ReviewDb db) throws OrmException {
 | 
				
			||||||
    groupMap = new HashMap<AccountGroup.Id, AccountGroup>();
 | 
					    groupMap = new HashMap<AccountGroup.Id, GroupReference>();
 | 
				
			||||||
    List<AccountGroup> groups = db.accountGroups().all().toList();
 | 
					    List<AccountGroup> groups = db.accountGroups().all().toList();
 | 
				
			||||||
    for (AccountGroup g : groups) {
 | 
					    for (AccountGroup g : groups) {
 | 
				
			||||||
      if (g.getId().equals(systemConfig.ownerGroupId)) {
 | 
					      if (g.getId().equals(systemConfig.ownerGroupId)) {
 | 
				
			||||||
        g.setGroupUUID(AccountGroup.PROJECT_OWNERS);
 | 
					        g.setGroupUUID(AccountGroup.PROJECT_OWNERS);
 | 
				
			||||||
 | 
					        projectOwners = GroupReference.forGroup(g);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      } else if (g.getId().equals(systemConfig.anonymousGroupId)) {
 | 
					      } else if (g.getId().equals(systemConfig.anonymousGroupId)) {
 | 
				
			||||||
        g.setGroupUUID(AccountGroup.ANONYMOUS_USERS);
 | 
					        g.setGroupUUID(AccountGroup.ANONYMOUS_USERS);
 | 
				
			||||||
@@ -82,7 +129,7 @@ class Schema_53 extends SchemaVersion {
 | 
				
			|||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        g.setGroupUUID(GroupUUID.make(g.getName(), serverUser));
 | 
					        g.setGroupUUID(GroupUUID.make(g.getName(), serverUser));
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      groupMap.put(g.getId(), g);
 | 
					      groupMap.put(g.getId(), GroupReference.forGroup(g));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    db.accountGroups().update(groups);
 | 
					    db.accountGroups().update(groups);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -92,7 +139,7 @@ class Schema_53 extends SchemaVersion {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private AccountGroup.UUID toUUID(AccountGroup.Id id) {
 | 
					  private AccountGroup.UUID toUUID(AccountGroup.Id id) {
 | 
				
			||||||
    return groupMap.get(id).getGroupUUID();
 | 
					    return groupMap.get(id).getUUID();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private void exportProjectConfig(ReviewDb db) throws OrmException,
 | 
					  private void exportProjectConfig(ReviewDb db) throws OrmException,
 | 
				
			||||||
@@ -123,6 +170,16 @@ class Schema_53 extends SchemaVersion {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        ProjectConfig config = ProjectConfig.read(md);
 | 
					        ProjectConfig config = ProjectConfig.read(md);
 | 
				
			||||||
        loadProject(rs, config.getProject());
 | 
					        loadProject(rs, config.getProject());
 | 
				
			||||||
 | 
					        config.getAccessSections().clear();
 | 
				
			||||||
 | 
					        convertRights(config);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Grant out read on the config branch by default.
 | 
				
			||||||
 | 
					        //
 | 
				
			||||||
 | 
					        if (config.getProject().getNameKey().equals(systemConfig.wildProjectName)) {
 | 
				
			||||||
 | 
					          AccessSection meta = config.getAccessSection(GitRepositoryManager.REF_CONFIG, true);
 | 
				
			||||||
 | 
					          Permission read = meta.getPermission(READ, true);
 | 
				
			||||||
 | 
					          read.getRule(config.resolve(projectOwners), true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        md.setMessage("Import project configuration from SQL\n");
 | 
					        md.setMessage("Import project configuration from SQL\n");
 | 
				
			||||||
        if (!config.commit(md)) {
 | 
					        if (!config.commit(md)) {
 | 
				
			||||||
@@ -169,4 +226,248 @@ class Schema_53 extends SchemaVersion {
 | 
				
			|||||||
    project.setUseContentMerge("Y".equals(rs.getString("use_content_merge")));
 | 
					    project.setUseContentMerge("Y".equals(rs.getString("use_content_merge")));
 | 
				
			||||||
    project.setParentName(rs.getString("parent_name"));
 | 
					    project.setParentName(rs.getString("parent_name"));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void readOldRefRights(ReviewDb db) throws SQLException {
 | 
				
			||||||
 | 
					    rightsByProject = new HashMap<Project.NameKey, List<OldRefRight>>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
 | 
				
			||||||
 | 
					    ResultSet rs = stmt.executeQuery("SELECT * FROM ref_rights");
 | 
				
			||||||
 | 
					    while (rs.next()) {
 | 
				
			||||||
 | 
					      OldRefRight right = new OldRefRight(rs);
 | 
				
			||||||
 | 
					      if (right.group == null || right.category == null) {
 | 
				
			||||||
 | 
					        continue;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      List<OldRefRight> list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      list = rightsByProject.get(right.project);
 | 
				
			||||||
 | 
					      if (list == null) {
 | 
				
			||||||
 | 
					        list = new ArrayList<OldRefRight>();
 | 
				
			||||||
 | 
					        rightsByProject.put(right.project, list);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      list.add(right);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    rs.close();
 | 
				
			||||||
 | 
					    stmt.close();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void readProjectParents(ReviewDb db) throws SQLException {
 | 
				
			||||||
 | 
					    parentsByProject = new HashMap<Project.NameKey, Project.NameKey>();
 | 
				
			||||||
 | 
					    Statement stmt = ((JdbcSchema) db).getConnection().createStatement();
 | 
				
			||||||
 | 
					    ResultSet rs = stmt.executeQuery("SELECT * FROM projects");
 | 
				
			||||||
 | 
					    while (rs.next()) {
 | 
				
			||||||
 | 
					      String name = rs.getString("name");
 | 
				
			||||||
 | 
					      String parent_name = rs.getString("parent_name");
 | 
				
			||||||
 | 
					      if (parent_name == null) {
 | 
				
			||||||
 | 
					        parent_name = systemConfig.wildProjectName.get();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      parentsByProject.put(new Project.NameKey(name), //
 | 
				
			||||||
 | 
					          new Project.NameKey(parent_name));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    rs.close();
 | 
				
			||||||
 | 
					    stmt.close();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void convertRights(ProjectConfig config) {
 | 
				
			||||||
 | 
					    List<OldRefRight> myRights =
 | 
				
			||||||
 | 
					        rightsByProject.get(config.getProject().getNameKey());
 | 
				
			||||||
 | 
					    if (myRights == null) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (OldRefRight old : myRights) {
 | 
				
			||||||
 | 
					      AccessSection section = config.getAccessSection(old.ref_pattern, true);
 | 
				
			||||||
 | 
					      GroupReference group = config.resolve(old.group);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (OLD_SUBMIT.equals(old.category)) {
 | 
				
			||||||
 | 
					        PermissionRule submit = rule(group);
 | 
				
			||||||
 | 
					        submit.setDeny(old.max_value <= 0);
 | 
				
			||||||
 | 
					        add(section, SUBMIT, old.exclusive, submit);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } else if (OLD_READ.equals(old.category)) {
 | 
				
			||||||
 | 
					        if (old.exclusive) {
 | 
				
			||||||
 | 
					          section.getPermission(READ, true).setExclusiveGroup(true);
 | 
				
			||||||
 | 
					          newChangePermission(config, old.ref_pattern).setExclusiveGroup(true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        PermissionRule read = rule(group);
 | 
				
			||||||
 | 
					        read.setDeny(old.max_value <= 0);
 | 
				
			||||||
 | 
					        add(section, READ, old.exclusive, read);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (3 <= old.max_value) {
 | 
				
			||||||
 | 
					          newMergePermission(config, old.ref_pattern).add(rule(group));
 | 
				
			||||||
 | 
					        } else if (3 <= inheritedMax(config, old)) {
 | 
				
			||||||
 | 
					          newMergePermission(config, old.ref_pattern).add(deny(group));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (2 <= old.max_value) {
 | 
				
			||||||
 | 
					          newChangePermission(config, old.ref_pattern).add(rule(group));
 | 
				
			||||||
 | 
					        } else if (2 <= inheritedMax(config, old)) {
 | 
				
			||||||
 | 
					          newChangePermission(config, old.ref_pattern).add(deny(group));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } else if (OLD_OWN.equals(old.category)) {
 | 
				
			||||||
 | 
					        add(section, OWNER, false, rule(group));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } else if (OLD_PUSH_TAG.equals(old.category)) {
 | 
				
			||||||
 | 
					        PermissionRule push = rule(group);
 | 
				
			||||||
 | 
					        push.setDeny(old.max_value <= 0);
 | 
				
			||||||
 | 
					        add(section, PUSH_TAG, old.exclusive, push);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } else if (OLD_PUSH_HEAD.equals(old.category)) {
 | 
				
			||||||
 | 
					        if (old.exclusive) {
 | 
				
			||||||
 | 
					          section.getPermission(PUSH, true).setExclusiveGroup(true);
 | 
				
			||||||
 | 
					          section.getPermission(CREATE, true).setExclusiveGroup(true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        PermissionRule push = rule(group);
 | 
				
			||||||
 | 
					        push.setDeny(old.max_value <= 0);
 | 
				
			||||||
 | 
					        push.setForce(3 <= old.max_value);
 | 
				
			||||||
 | 
					        add(section, PUSH, old.exclusive, push);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (2 <= old.max_value) {
 | 
				
			||||||
 | 
					          add(section, CREATE, old.exclusive, rule(group));
 | 
				
			||||||
 | 
					        } else if (2 <= inheritedMax(config, old)) {
 | 
				
			||||||
 | 
					          add(section, CREATE, old.exclusive, deny(group));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } else if (OLD_FORGE_IDENTITY.equals(old.category)) {
 | 
				
			||||||
 | 
					        if (old.exclusive) {
 | 
				
			||||||
 | 
					          section.getPermission(FORGE_AUTHOR, true).setExclusiveGroup(true);
 | 
				
			||||||
 | 
					          section.getPermission(FORGE_COMMITTER, true).setExclusiveGroup(true);
 | 
				
			||||||
 | 
					          section.getPermission(FORGE_SERVER, true).setExclusiveGroup(true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (1 <= old.max_value) {
 | 
				
			||||||
 | 
					          add(section, FORGE_AUTHOR, old.exclusive, rule(group));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (2 <= old.max_value) {
 | 
				
			||||||
 | 
					          add(section, FORGE_COMMITTER, old.exclusive, rule(group));
 | 
				
			||||||
 | 
					        } else if (2 <= inheritedMax(config, old)) {
 | 
				
			||||||
 | 
					          add(section, FORGE_COMMITTER, old.exclusive, deny(group));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (3 <= old.max_value) {
 | 
				
			||||||
 | 
					          add(section, FORGE_SERVER, old.exclusive, rule(group));
 | 
				
			||||||
 | 
					        } else if (3 <= inheritedMax(config, old)) {
 | 
				
			||||||
 | 
					          add(section, FORGE_SERVER, old.exclusive, deny(group));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        PermissionRule rule = rule(group);
 | 
				
			||||||
 | 
					        rule.setRange(old.min_value, old.max_value);
 | 
				
			||||||
 | 
					        if (old.min_value == 0 && old.max_value == 0) {
 | 
				
			||||||
 | 
					          rule.setDeny(true);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        add(section, LABEL + varNameOf(old.category), old.exclusive, rule);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static Permission newChangePermission(ProjectConfig config,
 | 
				
			||||||
 | 
					      String name) {
 | 
				
			||||||
 | 
					    if (name.startsWith(AccessSection.REGEX_PREFIX)) {
 | 
				
			||||||
 | 
					      name = AccessSection.REGEX_PREFIX
 | 
				
			||||||
 | 
					          + "refs/for/"
 | 
				
			||||||
 | 
					          + name.substring(AccessSection.REGEX_PREFIX.length());
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      name = "refs/for/" + name;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return config.getAccessSection(name, true).getPermission(PUSH, true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static Permission newMergePermission(ProjectConfig config,
 | 
				
			||||||
 | 
					      String name) {
 | 
				
			||||||
 | 
					    if (name.startsWith(AccessSection.REGEX_PREFIX)) {
 | 
				
			||||||
 | 
					      name = AccessSection.REGEX_PREFIX
 | 
				
			||||||
 | 
					          + "refs/for/"
 | 
				
			||||||
 | 
					          + name.substring(AccessSection.REGEX_PREFIX.length());
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      name = "refs/for/" + name;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return config.getAccessSection(name, true).getPermission(PUSH_MERGE, true);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static PermissionRule rule(GroupReference group) {
 | 
				
			||||||
 | 
					    return new PermissionRule(group);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static PermissionRule deny(GroupReference group) {
 | 
				
			||||||
 | 
					    PermissionRule rule = rule(group);
 | 
				
			||||||
 | 
					    rule.setDeny(true);
 | 
				
			||||||
 | 
					    return rule;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private int inheritedMax(ProjectConfig config, OldRefRight old) {
 | 
				
			||||||
 | 
					    int max = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    String ref = old.ref_pattern;
 | 
				
			||||||
 | 
					    String category = old.category;
 | 
				
			||||||
 | 
					    AccountGroup.UUID group = old.group.getUUID();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Project.NameKey project = config.getProject().getParent();
 | 
				
			||||||
 | 
					    if (project == null) {
 | 
				
			||||||
 | 
					      project = systemConfig.wildProjectName;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    do {
 | 
				
			||||||
 | 
					      List<OldRefRight> rights = rightsByProject.get(project);
 | 
				
			||||||
 | 
					      if (rights != null) {
 | 
				
			||||||
 | 
					        for (OldRefRight r : rights) {
 | 
				
			||||||
 | 
					          if (r.ref_pattern.equals(ref) //
 | 
				
			||||||
 | 
					              && r.group.getUUID().equals(group) //
 | 
				
			||||||
 | 
					              && r.category.equals(category)) {
 | 
				
			||||||
 | 
					            max = Math.max(max, r.max_value);
 | 
				
			||||||
 | 
					            break;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      project = parentsByProject.get(project);
 | 
				
			||||||
 | 
					    } while (!project.equals(systemConfig.wildProjectName));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return max;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private String varNameOf(String id) {
 | 
				
			||||||
 | 
					    ApprovalCategory category = categoryMap.get(new ApprovalCategory.Id(id));
 | 
				
			||||||
 | 
					    if (category == null) {
 | 
				
			||||||
 | 
					      category = new ApprovalCategory(new ApprovalCategory.Id(id), id);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return category.getLabelName();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static void add(AccessSection section, String name,
 | 
				
			||||||
 | 
					      boolean exclusive, PermissionRule rule) {
 | 
				
			||||||
 | 
					    Permission p = section.getPermission(name, true);
 | 
				
			||||||
 | 
					    if (exclusive) {
 | 
				
			||||||
 | 
					      p.setExclusiveGroup(true);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    p.add(rule);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private class OldRefRight {
 | 
				
			||||||
 | 
					    final int min_value;
 | 
				
			||||||
 | 
					    final int max_value;
 | 
				
			||||||
 | 
					    final String ref_pattern;
 | 
				
			||||||
 | 
					    final boolean exclusive;
 | 
				
			||||||
 | 
					    final GroupReference group;
 | 
				
			||||||
 | 
					    final String category;
 | 
				
			||||||
 | 
					    final Project.NameKey project;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    OldRefRight(ResultSet rs) throws SQLException {
 | 
				
			||||||
 | 
					      min_value = rs.getInt("min_value");
 | 
				
			||||||
 | 
					      max_value = rs.getInt("max_value");
 | 
				
			||||||
 | 
					      project = new Project.NameKey(rs.getString("project_name"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      String r = rs.getString("ref_pattern");
 | 
				
			||||||
 | 
					      exclusive = r.startsWith("-");
 | 
				
			||||||
 | 
					      if (exclusive) {
 | 
				
			||||||
 | 
					        r = r.substring(1);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      ref_pattern = r;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      category = rs.getString("category_id");
 | 
				
			||||||
 | 
					      group = groupMap.get(new AccountGroup.Id(rs.getInt("group_id")));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,11 +15,10 @@
 | 
				
			|||||||
package com.google.gerrit.server.workflow;
 | 
					package com.google.gerrit.server.workflow;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.gerrit.common.data.ApprovalType;
 | 
					import com.google.gerrit.common.data.ApprovalType;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.Permission;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
					import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
				
			||||||
import com.google.gerrit.reviewdb.PatchSetApproval;
 | 
					import com.google.gerrit.reviewdb.PatchSetApproval;
 | 
				
			||||||
import com.google.gerrit.reviewdb.RefRight;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.CurrentUser;
 | 
					import com.google.gerrit.server.CurrentUser;
 | 
				
			||||||
import com.google.gerrit.server.project.RefControl;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
@@ -29,7 +28,6 @@ public abstract class CategoryFunction {
 | 
				
			|||||||
  private static Map<String, CategoryFunction> all =
 | 
					  private static Map<String, CategoryFunction> all =
 | 
				
			||||||
      new HashMap<String, CategoryFunction>();
 | 
					      new HashMap<String, CategoryFunction>();
 | 
				
			||||||
  static {
 | 
					  static {
 | 
				
			||||||
    all.put(SubmitFunction.NAME, new SubmitFunction());
 | 
					 | 
				
			||||||
    all.put(MaxWithBlock.NAME, new MaxWithBlock());
 | 
					    all.put(MaxWithBlock.NAME, new MaxWithBlock());
 | 
				
			||||||
    all.put(MaxNoBlock.NAME, new MaxNoBlock());
 | 
					    all.put(MaxNoBlock.NAME, new MaxNoBlock());
 | 
				
			||||||
    all.put(NoOpFunction.NAME, new NoOpFunction());
 | 
					    all.put(NoOpFunction.NAME, new NoOpFunction());
 | 
				
			||||||
@@ -44,21 +42,10 @@ public abstract class CategoryFunction {
 | 
				
			|||||||
   *         is not known to Gerrit and thus cannot be executed.
 | 
					   *         is not known to Gerrit and thus cannot be executed.
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  public static CategoryFunction forCategory(final ApprovalCategory category) {
 | 
					  public static CategoryFunction forCategory(final ApprovalCategory category) {
 | 
				
			||||||
    final CategoryFunction r = forName(category.getFunctionName());
 | 
					    final CategoryFunction r = all.get(category.getFunctionName());
 | 
				
			||||||
    return r != null ? r : new NoOpFunction();
 | 
					    return r != null ? r : new NoOpFunction();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					 | 
				
			||||||
   * Locate a function by name.
 | 
					 | 
				
			||||||
   *
 | 
					 | 
				
			||||||
   * @param functionName the function's unique name.
 | 
					 | 
				
			||||||
   * @return the function implementation; null if the function is not known to
 | 
					 | 
				
			||||||
   *         Gerrit and thus cannot be executed.
 | 
					 | 
				
			||||||
   */
 | 
					 | 
				
			||||||
  public static CategoryFunction forName(final String functionName) {
 | 
					 | 
				
			||||||
    return all.get(functionName);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
   * Normalize ChangeApprovals and set the valid flag for this category.
 | 
					   * Normalize ChangeApprovals and set the valid flag for this category.
 | 
				
			||||||
   * <p>
 | 
					   * <p>
 | 
				
			||||||
@@ -92,13 +79,8 @@ public abstract class CategoryFunction {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  public boolean isValid(final CurrentUser user, final ApprovalType at,
 | 
					  public boolean isValid(final CurrentUser user, final ApprovalType at,
 | 
				
			||||||
      final FunctionState state) {
 | 
					      final FunctionState state) {
 | 
				
			||||||
    RefControl rc = state.controlFor(user);
 | 
					    return !state.controlFor(user) //
 | 
				
			||||||
    for (final RefRight pr : rc.getApplicableRights(at.getCategory().getId())) {
 | 
					        .getRange(Permission.forLabel(at.getCategory().getLabelName())) //
 | 
				
			||||||
      if (user.getEffectiveGroups().contains(pr.getAccountGroupUUID())
 | 
					        .isEmpty();
 | 
				
			||||||
          && (pr.getMinValue() < 0 || pr.getMaxValue() > 0)) {
 | 
					 | 
				
			||||||
        return true;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -16,13 +16,13 @@ package com.google.gerrit.server.workflow;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.google.gerrit.common.data.ApprovalType;
 | 
					import com.google.gerrit.common.data.ApprovalType;
 | 
				
			||||||
import com.google.gerrit.common.data.ApprovalTypes;
 | 
					import com.google.gerrit.common.data.ApprovalTypes;
 | 
				
			||||||
import com.google.gerrit.reviewdb.AccountGroup;
 | 
					import com.google.gerrit.common.data.Permission;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.PermissionRange;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
					import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
 | 
					import com.google.gerrit.reviewdb.ApprovalCategoryValue;
 | 
				
			||||||
import com.google.gerrit.reviewdb.Change;
 | 
					import com.google.gerrit.reviewdb.Change;
 | 
				
			||||||
import com.google.gerrit.reviewdb.PatchSet;
 | 
					import com.google.gerrit.reviewdb.PatchSet;
 | 
				
			||||||
import com.google.gerrit.reviewdb.PatchSetApproval;
 | 
					import com.google.gerrit.reviewdb.PatchSetApproval;
 | 
				
			||||||
import com.google.gerrit.reviewdb.RefRight;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory.Id;
 | 
					import com.google.gerrit.reviewdb.ApprovalCategory.Id;
 | 
				
			||||||
import com.google.gerrit.server.CurrentUser;
 | 
					import com.google.gerrit.server.CurrentUser;
 | 
				
			||||||
import com.google.gerrit.server.IdentifiedUser;
 | 
					import com.google.gerrit.server.IdentifiedUser;
 | 
				
			||||||
@@ -139,28 +139,12 @@ public class FunctionState {
 | 
				
			|||||||
   * of them is used.
 | 
					   * of them is used.
 | 
				
			||||||
   * <p>
 | 
					   * <p>
 | 
				
			||||||
   */
 | 
					   */
 | 
				
			||||||
  private void applyRightFloor(final PatchSetApproval a) {
 | 
					  private void applyRightFloor(final ApprovalType at, final PatchSetApproval a) {
 | 
				
			||||||
 | 
					    final ApprovalCategory category = at.getCategory();
 | 
				
			||||||
 | 
					    final String permission = Permission.forLabel(category.getLabelName());
 | 
				
			||||||
    final IdentifiedUser user = userFactory.create(a.getAccountId());
 | 
					    final IdentifiedUser user = userFactory.create(a.getAccountId());
 | 
				
			||||||
    RefControl rc = controlFor(user);
 | 
					    final PermissionRange range = controlFor(user).getRange(permission);
 | 
				
			||||||
 | 
					    a.setValue((short) range.squash(a.getValue()));
 | 
				
			||||||
    // Find the maximal range actually granted to the user.
 | 
					 | 
				
			||||||
    //
 | 
					 | 
				
			||||||
    short minAllowed = 0, maxAllowed = 0;
 | 
					 | 
				
			||||||
    for (final RefRight r : rc.getApplicableRights(a.getCategoryId())) {
 | 
					 | 
				
			||||||
      final AccountGroup.UUID grp = r.getAccountGroupUUID();
 | 
					 | 
				
			||||||
      if (user.getEffectiveGroups().contains(grp)) {
 | 
					 | 
				
			||||||
        minAllowed = (short) Math.min(minAllowed, r.getMinValue());
 | 
					 | 
				
			||||||
        maxAllowed = (short) Math.max(maxAllowed, r.getMaxValue());
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Normalize the value into that range.
 | 
					 | 
				
			||||||
    //
 | 
					 | 
				
			||||||
    if (a.getValue() < minAllowed) {
 | 
					 | 
				
			||||||
      a.setValue(minAllowed);
 | 
					 | 
				
			||||||
    } else if (a.getValue() > maxAllowed) {
 | 
					 | 
				
			||||||
      a.setValue(maxAllowed);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  RefControl controlFor(final CurrentUser user) {
 | 
					  RefControl controlFor(final CurrentUser user) {
 | 
				
			||||||
@@ -172,7 +156,7 @@ public class FunctionState {
 | 
				
			|||||||
  /** Run <code>applyTypeFloor</code>, <code>applyRightFloor</code>. */
 | 
					  /** Run <code>applyTypeFloor</code>, <code>applyRightFloor</code>. */
 | 
				
			||||||
  public void normalize(final ApprovalType at, final PatchSetApproval ca) {
 | 
					  public void normalize(final ApprovalType at, final PatchSetApproval ca) {
 | 
				
			||||||
    applyTypeFloor(at, ca);
 | 
					    applyTypeFloor(at, ca);
 | 
				
			||||||
    applyRightFloor(ca);
 | 
					    applyRightFloor(at, ca);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static Id id(final ApprovalType at) {
 | 
					  private static Id id(final ApprovalType at) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,68 +0,0 @@
 | 
				
			|||||||
// Copyright (C) 2008 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.workflow;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import com.google.gerrit.common.data.ApprovalType;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.Change;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.RefRight;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.CurrentUser;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.project.RefControl;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Computes if the submit function can be used.
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * In order to be considered "approved" this function requires that all approval
 | 
					 | 
				
			||||||
 * categories with a position >= 0 (that is any whose
 | 
					 | 
				
			||||||
 * {@link ApprovalCategory#isAction()} method returns false) is valid and that
 | 
					 | 
				
			||||||
 * the change state be {@link Change.Status#NEW}.
 | 
					 | 
				
			||||||
 * <p>
 | 
					 | 
				
			||||||
 * This is mostly useful for actions, like {@link ApprovalCategory#SUBMIT}.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
public class SubmitFunction extends CategoryFunction {
 | 
					 | 
				
			||||||
  public static String NAME = "Submit";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public void run(final ApprovalType at, final FunctionState state) {
 | 
					 | 
				
			||||||
    state.valid(at, valid(at, state));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					 | 
				
			||||||
  public boolean isValid(final CurrentUser user, final ApprovalType at,
 | 
					 | 
				
			||||||
      final FunctionState state) {
 | 
					 | 
				
			||||||
    if (valid(at, state)) {
 | 
					 | 
				
			||||||
      RefControl rc = state.controlFor(user);
 | 
					 | 
				
			||||||
      for (final RefRight pr : rc.getApplicableRights(at.getCategory().getId())) {
 | 
					 | 
				
			||||||
        if (user.getEffectiveGroups().contains(pr.getAccountGroupUUID())
 | 
					 | 
				
			||||||
            && pr.getMaxValue() > 0) {
 | 
					 | 
				
			||||||
          return true;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private static boolean valid(final ApprovalType at, final FunctionState state) {
 | 
					 | 
				
			||||||
    if (state.getChange().getStatus() != Change.Status.NEW) {
 | 
					 | 
				
			||||||
      return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    for (final ApprovalType t : state.getApprovalTypes()) {
 | 
					 | 
				
			||||||
      if (!state.isValid(t)) {
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    return true;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -0,0 +1,191 @@
 | 
				
			|||||||
 | 
					// Copyright (C) 2011 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.git;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import static org.junit.Assert.assertEquals;
 | 
				
			||||||
 | 
					import static org.junit.Assert.assertFalse;
 | 
				
			||||||
 | 
					import static org.junit.Assert.assertNotNull;
 | 
				
			||||||
 | 
					import static org.junit.Assert.assertNull;
 | 
				
			||||||
 | 
					import static org.junit.Assert.assertTrue;
 | 
				
			||||||
 | 
					import static org.junit.Assert.fail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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.Project;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.eclipse.jgit.errors.ConfigInvalidException;
 | 
				
			||||||
 | 
					import org.eclipse.jgit.errors.IncorrectObjectTypeException;
 | 
				
			||||||
 | 
					import org.eclipse.jgit.errors.MissingObjectException;
 | 
				
			||||||
 | 
					import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase;
 | 
				
			||||||
 | 
					import org.eclipse.jgit.junit.TestRepository;
 | 
				
			||||||
 | 
					import org.eclipse.jgit.lib.Ref;
 | 
				
			||||||
 | 
					import org.eclipse.jgit.lib.RefUpdate;
 | 
				
			||||||
 | 
					import org.eclipse.jgit.lib.Repository;
 | 
				
			||||||
 | 
					import org.eclipse.jgit.revwalk.RevCommit;
 | 
				
			||||||
 | 
					import org.eclipse.jgit.revwalk.RevObject;
 | 
				
			||||||
 | 
					import org.eclipse.jgit.util.RawParseUtils;
 | 
				
			||||||
 | 
					import org.junit.Before;
 | 
				
			||||||
 | 
					import org.junit.Test;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ProjectConfigTest extends LocalDiskRepositoryTestCase {
 | 
				
			||||||
 | 
					  private final GroupReference developers = new GroupReference(
 | 
				
			||||||
 | 
					      new AccountGroup.UUID("X"), "Developers");
 | 
				
			||||||
 | 
					  private final GroupReference staff = new GroupReference(
 | 
				
			||||||
 | 
					      new AccountGroup.UUID("Y"), "Staff");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private Repository db;
 | 
				
			||||||
 | 
					  private TestRepository<Repository> util;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Override
 | 
				
			||||||
 | 
					  @Before
 | 
				
			||||||
 | 
					  public void setUp() throws Exception {
 | 
				
			||||||
 | 
					    super.setUp();
 | 
				
			||||||
 | 
					    db = createBareRepository();
 | 
				
			||||||
 | 
					    util = new TestRepository<Repository>(db);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Test
 | 
				
			||||||
 | 
					  public void testReadConfig() throws Exception {
 | 
				
			||||||
 | 
					    RevCommit rev = util.commit(util.tree( //
 | 
				
			||||||
 | 
					        util.file("groups", util.blob(group(developers))), //
 | 
				
			||||||
 | 
					        util.file("project.config", util.blob(""//
 | 
				
			||||||
 | 
					            + "[access \"refs/heads/*\"]\n" //
 | 
				
			||||||
 | 
					            + "  exclusiveGroupPermissions = read submit create\n" //
 | 
				
			||||||
 | 
					            + "  submit = group Developers\n" //
 | 
				
			||||||
 | 
					            + "  push = group Developers\n" //
 | 
				
			||||||
 | 
					            + "  read = group Developers\n")) //
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ProjectConfig cfg = read(rev);
 | 
				
			||||||
 | 
					    AccessSection section = cfg.getAccessSection("refs/heads/*");
 | 
				
			||||||
 | 
					    assertNotNull("has refs/heads/*", section);
 | 
				
			||||||
 | 
					    assertNull("no refs/*", cfg.getAccessSection("refs/*"));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Permission create = section.getPermission(Permission.CREATE);
 | 
				
			||||||
 | 
					    Permission submit = section.getPermission(Permission.SUBMIT);
 | 
				
			||||||
 | 
					    Permission read = section.getPermission(Permission.READ);
 | 
				
			||||||
 | 
					    Permission push = section.getPermission(Permission.PUSH);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assertTrue(create.getExclusiveGroup());
 | 
				
			||||||
 | 
					    assertTrue(submit.getExclusiveGroup());
 | 
				
			||||||
 | 
					    assertTrue(read.getExclusiveGroup());
 | 
				
			||||||
 | 
					    assertFalse(push.getExclusiveGroup());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Test
 | 
				
			||||||
 | 
					  public void testEditConfig() throws Exception {
 | 
				
			||||||
 | 
					    RevCommit rev = util.commit(util.tree( //
 | 
				
			||||||
 | 
					        util.file("groups", util.blob(group(developers))), //
 | 
				
			||||||
 | 
					        util.file("project.config", util.blob(""//
 | 
				
			||||||
 | 
					            + "[access \"refs/heads/*\"]\n" //
 | 
				
			||||||
 | 
					            + "  exclusiveGroupPermissions = read submit\n" //
 | 
				
			||||||
 | 
					            + "  submit = group Developers\n" //
 | 
				
			||||||
 | 
					            + "  upload = group Developers\n" //
 | 
				
			||||||
 | 
					            + "  read = group Developers\n")) //
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					    update(rev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ProjectConfig cfg = read(rev);
 | 
				
			||||||
 | 
					    AccessSection section = cfg.getAccessSection("refs/heads/*");
 | 
				
			||||||
 | 
					    Permission submit = section.getPermission(Permission.SUBMIT);
 | 
				
			||||||
 | 
					    submit.add(new PermissionRule(cfg.resolve(staff)));
 | 
				
			||||||
 | 
					    rev = commit(cfg);
 | 
				
			||||||
 | 
					    assertEquals(""//
 | 
				
			||||||
 | 
					        + "[access \"refs/heads/*\"]\n" //
 | 
				
			||||||
 | 
					        + "  exclusiveGroupPermissions = read submit\n" //
 | 
				
			||||||
 | 
					        + "  submit = group Developers\n" //
 | 
				
			||||||
 | 
					        + "\tsubmit = group Staff\n" //
 | 
				
			||||||
 | 
					        + "  upload = group Developers\n" //
 | 
				
			||||||
 | 
					        + "  read = group Developers\n", text(rev, "project.config"));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @Test
 | 
				
			||||||
 | 
					  public void testEditConfigMissingGroupTableEntry() throws Exception {
 | 
				
			||||||
 | 
					    RevCommit rev = util.commit(util.tree( //
 | 
				
			||||||
 | 
					        util.file("groups", util.blob(group(developers))), //
 | 
				
			||||||
 | 
					        util.file("project.config", util.blob(""//
 | 
				
			||||||
 | 
					            + "[access \"refs/heads/*\"]\n" //
 | 
				
			||||||
 | 
					            + "  exclusiveGroupPermissions = read submit\n" //
 | 
				
			||||||
 | 
					            + "  submit = group People Who Can Submit\n" //
 | 
				
			||||||
 | 
					            + "  upload = group Developers\n" //
 | 
				
			||||||
 | 
					            + "  read = group Developers\n")) //
 | 
				
			||||||
 | 
					        ));
 | 
				
			||||||
 | 
					    update(rev);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ProjectConfig cfg = read(rev);
 | 
				
			||||||
 | 
					    AccessSection section = cfg.getAccessSection("refs/heads/*");
 | 
				
			||||||
 | 
					    Permission submit = section.getPermission(Permission.SUBMIT);
 | 
				
			||||||
 | 
					    submit.add(new PermissionRule(cfg.resolve(staff)));
 | 
				
			||||||
 | 
					    rev = commit(cfg);
 | 
				
			||||||
 | 
					    assertEquals(""//
 | 
				
			||||||
 | 
					        + "[access \"refs/heads/*\"]\n" //
 | 
				
			||||||
 | 
					        + "  exclusiveGroupPermissions = read submit\n" //
 | 
				
			||||||
 | 
					        + "  submit = group People Who Can Submit\n" //
 | 
				
			||||||
 | 
					        + "\tsubmit = group Staff\n" //
 | 
				
			||||||
 | 
					        + "  upload = group Developers\n" //
 | 
				
			||||||
 | 
					        + "  read = group Developers\n", text(rev, "project.config"));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private ProjectConfig read(RevCommit rev) throws IOException,
 | 
				
			||||||
 | 
					      ConfigInvalidException {
 | 
				
			||||||
 | 
					    ProjectConfig cfg = new ProjectConfig(new Project.NameKey("test"));
 | 
				
			||||||
 | 
					    cfg.load(db, rev);
 | 
				
			||||||
 | 
					    return cfg;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private RevCommit commit(ProjectConfig cfg) throws IOException,
 | 
				
			||||||
 | 
					      MissingObjectException, IncorrectObjectTypeException {
 | 
				
			||||||
 | 
					    MetaDataUpdate md = new MetaDataUpdate(new NoReplication(), //
 | 
				
			||||||
 | 
					        cfg.getProject().getNameKey(), //
 | 
				
			||||||
 | 
					        db);
 | 
				
			||||||
 | 
					    util.tick(5);
 | 
				
			||||||
 | 
					    util.setAuthorAndCommitter(md.getCommitBuilder());
 | 
				
			||||||
 | 
					    md.setMessage("Edit\n");
 | 
				
			||||||
 | 
					    assertTrue("commit finished", cfg.commit(md));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Ref ref = db.getRef(GitRepositoryManager.REF_CONFIG);
 | 
				
			||||||
 | 
					    return util.getRevWalk().parseCommit(ref.getObjectId());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private void update(RevCommit rev) throws Exception {
 | 
				
			||||||
 | 
					    RefUpdate u = db.updateRef(GitRepositoryManager.REF_CONFIG);
 | 
				
			||||||
 | 
					    u.disableRefLog();
 | 
				
			||||||
 | 
					    u.setNewObjectId(rev);
 | 
				
			||||||
 | 
					    switch (u.forceUpdate()) {
 | 
				
			||||||
 | 
					      case FAST_FORWARD:
 | 
				
			||||||
 | 
					      case FORCED:
 | 
				
			||||||
 | 
					      case NEW:
 | 
				
			||||||
 | 
					      case NO_CHANGE:
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      default:
 | 
				
			||||||
 | 
					        fail("Cannot update ref for test: " + u.getResult());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private String text(RevCommit rev, String path) throws Exception {
 | 
				
			||||||
 | 
					    RevObject blob = util.get(rev.getTree(), path);
 | 
				
			||||||
 | 
					    byte[] data = db.open(blob).getCachedBytes(Integer.MAX_VALUE);
 | 
				
			||||||
 | 
					    return RawParseUtils.decode(data);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  private static String group(GroupReference g) {
 | 
				
			||||||
 | 
					    return g.getUUID().get() + "\t" + g.getName() + "\n";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -14,23 +14,24 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
package com.google.gerrit.server.project;
 | 
					package com.google.gerrit.server.project;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.OWN;
 | 
					import static com.google.gerrit.common.data.Permission.OWNER;
 | 
				
			||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.READ;
 | 
					import static com.google.gerrit.common.data.Permission.PUSH;
 | 
				
			||||||
import static com.google.gerrit.reviewdb.ApprovalCategory.SUBMIT;
 | 
					import static com.google.gerrit.common.data.Permission.READ;
 | 
				
			||||||
 | 
					import static com.google.gerrit.common.data.Permission.SUBMIT;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.GroupReference;
 | 
				
			||||||
 | 
					import com.google.gerrit.common.data.PermissionRule;
 | 
				
			||||||
import com.google.gerrit.reviewdb.AccountGroup;
 | 
					import com.google.gerrit.reviewdb.AccountGroup;
 | 
				
			||||||
import com.google.gerrit.reviewdb.AccountProjectWatch;
 | 
					import com.google.gerrit.reviewdb.AccountProjectWatch;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.Change;
 | 
					import com.google.gerrit.reviewdb.Change;
 | 
				
			||||||
import com.google.gerrit.reviewdb.Project;
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
import com.google.gerrit.reviewdb.RefRight;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.SystemConfig;
 | 
					import com.google.gerrit.reviewdb.SystemConfig;
 | 
				
			||||||
import com.google.gerrit.reviewdb.RefRight.RefPattern;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.AccessPath;
 | 
					import com.google.gerrit.server.AccessPath;
 | 
				
			||||||
import com.google.gerrit.server.AnonymousUser;
 | 
					import com.google.gerrit.server.AnonymousUser;
 | 
				
			||||||
import com.google.gerrit.server.CurrentUser;
 | 
					import com.google.gerrit.server.CurrentUser;
 | 
				
			||||||
import com.google.gerrit.server.config.AuthConfig;
 | 
					import com.google.gerrit.server.config.AuthConfig;
 | 
				
			||||||
import com.google.gerrit.server.config.GerritServerConfig;
 | 
					import com.google.gerrit.server.config.GerritServerConfig;
 | 
				
			||||||
 | 
					import com.google.gerrit.server.git.ProjectConfig;
 | 
				
			||||||
import com.google.inject.AbstractModule;
 | 
					import com.google.inject.AbstractModule;
 | 
				
			||||||
import com.google.inject.Guice;
 | 
					import com.google.inject.Guice;
 | 
				
			||||||
import com.google.inject.Injector;
 | 
					import com.google.inject.Injector;
 | 
				
			||||||
@@ -41,19 +42,17 @@ import org.apache.commons.codec.binary.Base64;
 | 
				
			|||||||
import org.eclipse.jgit.lib.Config;
 | 
					import org.eclipse.jgit.lib.Config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.io.UnsupportedEncodingException;
 | 
					import java.io.UnsupportedEncodingException;
 | 
				
			||||||
import java.util.ArrayList;
 | 
					 | 
				
			||||||
import java.util.Arrays;
 | 
					import java.util.Arrays;
 | 
				
			||||||
import java.util.Collection;
 | 
					import java.util.Collection;
 | 
				
			||||||
import java.util.Collections;
 | 
					import java.util.Collections;
 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.HashMap;
 | 
				
			||||||
import java.util.HashSet;
 | 
					import java.util.HashSet;
 | 
				
			||||||
import java.util.List;
 | 
					 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class RefControlTest extends TestCase {
 | 
					public class RefControlTest extends TestCase {
 | 
				
			||||||
  public void testOwnerProject() {
 | 
					  public void testOwnerProject() {
 | 
				
			||||||
    grant(local, OWN, admin, "refs/*", 1);
 | 
					    grant(local, OWNER, admin, "refs/*");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ProjectControl uBlah = user(devs);
 | 
					    ProjectControl uBlah = user(devs);
 | 
				
			||||||
    ProjectControl uAdmin = user(devs, admin);
 | 
					    ProjectControl uAdmin = user(devs, admin);
 | 
				
			||||||
@@ -63,8 +62,8 @@ public class RefControlTest extends TestCase {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void testBranchDelegation1() {
 | 
					  public void testBranchDelegation1() {
 | 
				
			||||||
    grant(local, OWN, admin, "refs/*", 1);
 | 
					    grant(local, OWNER, admin, "refs/*");
 | 
				
			||||||
    grant(local, OWN, devs, "refs/heads/x/*", 1);
 | 
					    grant(local, OWNER, devs, "refs/heads/x/*");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ProjectControl uDev = user(devs);
 | 
					    ProjectControl uDev = user(devs);
 | 
				
			||||||
    assertFalse("not owner", uDev.isOwner());
 | 
					    assertFalse("not owner", uDev.isOwner());
 | 
				
			||||||
@@ -79,9 +78,10 @@ public class RefControlTest extends TestCase {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void testBranchDelegation2() {
 | 
					  public void testBranchDelegation2() {
 | 
				
			||||||
    grant(local, OWN, admin, "refs/*", 1);
 | 
					    grant(local, OWNER, admin, "refs/*");
 | 
				
			||||||
    grant(local, OWN, devs, "refs/heads/x/*", 1);
 | 
					    grant(local, OWNER, devs, "refs/heads/x/*");
 | 
				
			||||||
    grant(local, OWN, fixers, "-refs/heads/x/y/*", 1);
 | 
					    grant(local, OWNER, fixers, "refs/heads/x/y/*");
 | 
				
			||||||
 | 
					    doNotInherit(local, OWNER, "refs/heads/x/y/*");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ProjectControl uDev = user(devs);
 | 
					    ProjectControl uDev = user(devs);
 | 
				
			||||||
    assertFalse("not owner", uDev.isOwner());
 | 
					    assertFalse("not owner", uDev.isOwner());
 | 
				
			||||||
@@ -106,8 +106,11 @@ public class RefControlTest extends TestCase {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void testInheritRead_SingleBranchDeniesUpload() {
 | 
					  public void testInheritRead_SingleBranchDeniesUpload() {
 | 
				
			||||||
    grant(parent, READ, registered, "refs/*", 1, 2);
 | 
					    grant(parent, READ, registered, "refs/*");
 | 
				
			||||||
    grant(local, READ, registered, "-refs/heads/foobar", 1);
 | 
					    grant(parent, PUSH, registered, "refs/for/refs/*");
 | 
				
			||||||
 | 
					    grant(local, READ, registered, "refs/heads/foobar");
 | 
				
			||||||
 | 
					    doNotInherit(local, READ, "refs/heads/foobar");
 | 
				
			||||||
 | 
					    doNotInherit(local, PUSH, "refs/for/refs/heads/foobar");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ProjectControl u = user();
 | 
					    ProjectControl u = user();
 | 
				
			||||||
    assertTrue("can upload", u.canPushToAtLeastOneRef());
 | 
					    assertTrue("can upload", u.canPushToAtLeastOneRef());
 | 
				
			||||||
@@ -120,8 +123,9 @@ public class RefControlTest extends TestCase {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void testInheritRead_SingleBranchDoesNotOverrideInherited() {
 | 
					  public void testInheritRead_SingleBranchDoesNotOverrideInherited() {
 | 
				
			||||||
    grant(parent, READ, registered, "refs/*", 1, 2);
 | 
					    grant(parent, READ, registered, "refs/*");
 | 
				
			||||||
    grant(local, READ, registered, "refs/heads/foobar", 1);
 | 
					    grant(parent, PUSH, registered, "refs/for/refs/*");
 | 
				
			||||||
 | 
					    grant(local, READ, registered, "refs/heads/foobar");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ProjectControl u = user();
 | 
					    ProjectControl u = user();
 | 
				
			||||||
    assertTrue("can upload", u.canPushToAtLeastOneRef());
 | 
					    assertTrue("can upload", u.canPushToAtLeastOneRef());
 | 
				
			||||||
@@ -134,16 +138,16 @@ public class RefControlTest extends TestCase {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void testInheritRead_OverrideWithDeny() {
 | 
					  public void testInheritRead_OverrideWithDeny() {
 | 
				
			||||||
    grant(parent, READ, registered, "refs/*", 1);
 | 
					    grant(parent, READ, registered, "refs/*");
 | 
				
			||||||
    grant(local, READ, registered, "refs/*", 0);
 | 
					    grant(local, READ, registered, "refs/*").setDeny(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ProjectControl u = user();
 | 
					    ProjectControl u = user();
 | 
				
			||||||
    assertFalse("can't read", u.isVisible());
 | 
					    assertFalse("can't read", u.isVisible());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void testInheritRead_AppendWithDenyOfRef() {
 | 
					  public void testInheritRead_AppendWithDenyOfRef() {
 | 
				
			||||||
    grant(parent, READ, registered, "refs/*", 1);
 | 
					    grant(parent, READ, registered, "refs/*");
 | 
				
			||||||
    grant(local, READ, registered, "refs/heads/*", 0);
 | 
					    grant(local, READ, registered, "refs/heads/*").setDeny(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ProjectControl u = user();
 | 
					    ProjectControl u = user();
 | 
				
			||||||
    assertTrue("can read", u.isVisible());
 | 
					    assertTrue("can read", u.isVisible());
 | 
				
			||||||
@@ -153,9 +157,9 @@ public class RefControlTest extends TestCase {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void testInheritRead_OverridesAndDeniesOfRef() {
 | 
					  public void testInheritRead_OverridesAndDeniesOfRef() {
 | 
				
			||||||
    grant(parent, READ, registered, "refs/*", 1);
 | 
					    grant(parent, READ, registered, "refs/*");
 | 
				
			||||||
    grant(local, READ, registered, "refs/*", 0);
 | 
					    grant(local, READ, registered, "refs/*").setDeny(true);
 | 
				
			||||||
    grant(local, READ, registered, "refs/heads/*", -1, 1);
 | 
					    grant(local, READ, registered, "refs/heads/*");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ProjectControl u = user();
 | 
					    ProjectControl u = user();
 | 
				
			||||||
    assertTrue("can read", u.isVisible());
 | 
					    assertTrue("can read", u.isVisible());
 | 
				
			||||||
@@ -165,9 +169,9 @@ public class RefControlTest extends TestCase {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void testInheritSubmit_OverridesAndDeniesOfRef() {
 | 
					  public void testInheritSubmit_OverridesAndDeniesOfRef() {
 | 
				
			||||||
    grant(parent, SUBMIT, registered, "refs/*", 1);
 | 
					    grant(parent, SUBMIT, registered, "refs/*");
 | 
				
			||||||
    grant(local, SUBMIT, registered, "refs/*", 0);
 | 
					    grant(local, SUBMIT, registered, "refs/*").setDeny(true);
 | 
				
			||||||
    grant(local, SUBMIT, registered, "refs/heads/*", -1, 1);
 | 
					    grant(local, SUBMIT, registered, "refs/heads/*");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ProjectControl u = user();
 | 
					    ProjectControl u = user();
 | 
				
			||||||
    assertFalse("can't submit", u.controlForRef("refs/foobar").canSubmit());
 | 
					    assertFalse("can't submit", u.controlForRef("refs/foobar").canSubmit());
 | 
				
			||||||
@@ -176,8 +180,9 @@ public class RefControlTest extends TestCase {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void testCannotUploadToAnyRef() {
 | 
					  public void testCannotUploadToAnyRef() {
 | 
				
			||||||
    grant(parent, READ, registered, "refs/*", 1);
 | 
					    grant(parent, READ, registered, "refs/*");
 | 
				
			||||||
    grant(local, READ, devs, "refs/heads/*", 1, 2);
 | 
					    grant(local, READ, devs, "refs/heads/*");
 | 
				
			||||||
 | 
					    grant(local, PUSH, devs, "refs/for/refs/heads/*");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    ProjectControl u = user();
 | 
					    ProjectControl u = user();
 | 
				
			||||||
    assertFalse("cannot upload", u.canPushToAtLeastOneRef());
 | 
					    assertFalse("cannot upload", u.canPushToAtLeastOneRef());
 | 
				
			||||||
@@ -188,8 +193,8 @@ public class RefControlTest extends TestCase {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  // -----------------------------------------------------------------------
 | 
					  // -----------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private final Project.NameKey local = new Project.NameKey("test");
 | 
					  private ProjectConfig local;
 | 
				
			||||||
  private final Project.NameKey parent = new Project.NameKey("parent");
 | 
					  private ProjectConfig parent;
 | 
				
			||||||
  private final AccountGroup.UUID admin = new AccountGroup.UUID("test.admin");
 | 
					  private final AccountGroup.UUID admin = new AccountGroup.UUID("test.admin");
 | 
				
			||||||
  private final AccountGroup.UUID anonymous = AccountGroup.ANONYMOUS_USERS;
 | 
					  private final AccountGroup.UUID anonymous = AccountGroup.ANONYMOUS_USERS;
 | 
				
			||||||
  private final AccountGroup.UUID registered = AccountGroup.REGISTERED_USERS;
 | 
					  private final AccountGroup.UUID registered = AccountGroup.REGISTERED_USERS;
 | 
				
			||||||
@@ -201,9 +206,6 @@ public class RefControlTest extends TestCase {
 | 
				
			|||||||
  private final AuthConfig authConfig;
 | 
					  private final AuthConfig authConfig;
 | 
				
			||||||
  private final AnonymousUser anonymousUser;
 | 
					  private final AnonymousUser anonymousUser;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private final Map<AccountGroup.UUID, AccountGroup.Id> groupIds =
 | 
					 | 
				
			||||||
      new HashMap<AccountGroup.UUID, AccountGroup.Id>();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public RefControlTest() {
 | 
					  public RefControlTest() {
 | 
				
			||||||
    systemConfig = SystemConfig.create();
 | 
					    systemConfig = SystemConfig.create();
 | 
				
			||||||
    systemConfig.adminGroupUUID = admin;
 | 
					    systemConfig.adminGroupUUID = admin;
 | 
				
			||||||
@@ -231,14 +233,16 @@ public class RefControlTest extends TestCase {
 | 
				
			|||||||
    anonymousUser = injector.getInstance(AnonymousUser.class);
 | 
					    anonymousUser = injector.getInstance(AnonymousUser.class);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private List<RefRight> localRights;
 | 
					 | 
				
			||||||
  private List<RefRight> inheritedRights;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Override
 | 
					  @Override
 | 
				
			||||||
  protected void setUp() throws Exception {
 | 
					  public void setUp() throws Exception {
 | 
				
			||||||
    super.setUp();
 | 
					    super.setUp();
 | 
				
			||||||
    localRights = new ArrayList<RefRight>();
 | 
					
 | 
				
			||||||
    inheritedRights = new ArrayList<RefRight>();
 | 
					    parent = new ProjectConfig(new Project.NameKey("parent"));
 | 
				
			||||||
 | 
					    parent.createInMemory();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    local = new ProjectConfig(new Project.NameKey("local"));
 | 
				
			||||||
 | 
					    local.createInMemory();
 | 
				
			||||||
 | 
					    local.getProject().setParentName(parent.getProject().getName());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private static void assertOwner(String ref, ProjectControl u) {
 | 
					  private static void assertOwner(String ref, ProjectControl u) {
 | 
				
			||||||
@@ -249,33 +253,27 @@ public class RefControlTest extends TestCase {
 | 
				
			|||||||
    assertFalse("NOT OWN " + ref, u.controlForRef(ref).isOwner());
 | 
					    assertFalse("NOT OWN " + ref, u.controlForRef(ref).isOwner());
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private void grant(Project.NameKey project, ApprovalCategory.Id categoryId,
 | 
					  private PermissionRule grant(ProjectConfig project, String permissionName,
 | 
				
			||||||
      AccountGroup.UUID group, String ref, int maxValue) {
 | 
					      AccountGroup.UUID group, String ref) {
 | 
				
			||||||
    grant(project, categoryId, group, ref, maxValue, maxValue);
 | 
					    PermissionRule rule = newRule(project, group);
 | 
				
			||||||
 | 
					    project.getAccessSection(ref, true) //
 | 
				
			||||||
 | 
					        .getPermission(permissionName, true) //
 | 
				
			||||||
 | 
					        .add(rule);
 | 
				
			||||||
 | 
					    return rule;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private void grant(Project.NameKey project, ApprovalCategory.Id categoryId,
 | 
					  private void doNotInherit(ProjectConfig project, String permissionName,
 | 
				
			||||||
      AccountGroup.UUID groupUUID, String ref, int minValue, int maxValue) {
 | 
					      String ref) {
 | 
				
			||||||
    AccountGroup.Id groupId = groupIds.get(groupUUID);
 | 
					    project.getAccessSection(ref, true) //
 | 
				
			||||||
    if (groupId == null) {
 | 
					        .getPermission(permissionName, true) //
 | 
				
			||||||
      groupId = new AccountGroup.Id(groupIds.size() + 1);
 | 
					        .setExclusiveGroup(true);
 | 
				
			||||||
      groupIds.put(groupUUID, groupId);
 | 
					  }
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    RefRight right =
 | 
					  private PermissionRule newRule(ProjectConfig project, AccountGroup.UUID groupUUID) {
 | 
				
			||||||
        new RefRight(new RefRight.Key(project, new RefPattern(ref),
 | 
					    GroupReference group = new GroupReference(groupUUID, groupUUID.get());
 | 
				
			||||||
            categoryId, groupId));
 | 
					    group = project.resolve(group);
 | 
				
			||||||
    right.setAccountGroupUUID(groupUUID);
 | 
					 | 
				
			||||||
    right.setMinValue((short) minValue);
 | 
					 | 
				
			||||||
    right.setMaxValue((short) maxValue);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (project == parent) {
 | 
					    return new PermissionRule(group);
 | 
				
			||||||
      inheritedRights.add(right);
 | 
					 | 
				
			||||||
    } else if (project == local) {
 | 
					 | 
				
			||||||
      localRights.add(right);
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      fail("Unknown project key: " + project);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private ProjectControl user(AccountGroup.UUID... memberOf) {
 | 
					  private ProjectControl user(AccountGroup.UUID... memberOf) {
 | 
				
			||||||
@@ -291,15 +289,40 @@ public class RefControlTest extends TestCase {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private ProjectState newProjectState() {
 | 
					  private ProjectState newProjectState() {
 | 
				
			||||||
    ProjectCache projectCache = null;
 | 
					    final Map<Project.NameKey, ProjectState> all =
 | 
				
			||||||
 | 
					        new HashMap<Project.NameKey, ProjectState>();
 | 
				
			||||||
 | 
					    final ProjectCache projectCache = new ProjectCache() {
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public ProjectState get(Project.NameKey projectName) {
 | 
				
			||||||
 | 
					        return all.get(projectName);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public void evict(Project p) {
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public Iterable<Project.NameKey> all() {
 | 
				
			||||||
 | 
					        return Collections.emptySet();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public Iterable<Project.NameKey> byName(String prefix) {
 | 
				
			||||||
 | 
					        return Collections.emptySet();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      @Override
 | 
				
			||||||
 | 
					      public void onCreateProject(Project.NameKey newProjectName) {
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Project.NameKey wildProject = new Project.NameKey("-- All Projects --");
 | 
					    Project.NameKey wildProject = new Project.NameKey("-- All Projects --");
 | 
				
			||||||
    ProjectControl.AssistedFactory projectControlFactory = null;
 | 
					    ProjectControl.AssistedFactory projectControlFactory = null;
 | 
				
			||||||
    Project project = new Project(parent);
 | 
					    all.put(local.getProject().getNameKey(), new ProjectState(anonymousUser,
 | 
				
			||||||
    ProjectState ps =
 | 
					        projectCache, wildProject, projectControlFactory, local));
 | 
				
			||||||
        new ProjectState(anonymousUser, projectCache, wildProject,
 | 
					    all.put(parent.getProject().getNameKey(), new ProjectState(anonymousUser,
 | 
				
			||||||
            projectControlFactory, project, localRights);
 | 
					        projectCache, wildProject, projectControlFactory, parent));
 | 
				
			||||||
    ps.setInheritedRights(inheritedRights);
 | 
					    return all.get(local.getProject().getNameKey());
 | 
				
			||||||
    return ps;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private class MockUser extends CurrentUser {
 | 
					  private class MockUser extends CurrentUser {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,11 +17,8 @@ package com.google.gerrit.server.schema;
 | 
				
			|||||||
import com.google.gerrit.reviewdb.AccountGroup;
 | 
					import com.google.gerrit.reviewdb.AccountGroup;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
					import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategoryValue;
 | 
					import com.google.gerrit.reviewdb.ApprovalCategoryValue;
 | 
				
			||||||
import com.google.gerrit.reviewdb.RefRight;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.ReviewDb;
 | 
					import com.google.gerrit.reviewdb.ReviewDb;
 | 
				
			||||||
import com.google.gerrit.reviewdb.SystemConfig;
 | 
					import com.google.gerrit.reviewdb.SystemConfig;
 | 
				
			||||||
import com.google.gerrit.server.workflow.NoOpFunction;
 | 
					 | 
				
			||||||
import com.google.gerrit.server.workflow.SubmitFunction;
 | 
					 | 
				
			||||||
import com.google.gerrit.testutil.InMemoryDatabase;
 | 
					import com.google.gerrit.testutil.InMemoryDatabase;
 | 
				
			||||||
import com.google.gwtorm.client.OrmException;
 | 
					import com.google.gwtorm.client.OrmException;
 | 
				
			||||||
import com.google.gwtorm.jdbc.JdbcSchema;
 | 
					import com.google.gwtorm.jdbc.JdbcSchema;
 | 
				
			||||||
@@ -163,7 +160,6 @@ public class SchemaCreatorTest extends TestCase {
 | 
				
			|||||||
      assertEquals("R", cat.getAbbreviatedName());
 | 
					      assertEquals("R", cat.getAbbreviatedName());
 | 
				
			||||||
      assertEquals("MaxWithBlock", cat.getFunctionName());
 | 
					      assertEquals("MaxWithBlock", cat.getFunctionName());
 | 
				
			||||||
      assertTrue(cat.isCopyMinScore());
 | 
					      assertTrue(cat.isCopyMinScore());
 | 
				
			||||||
      assertFalse(cat.isAction());
 | 
					 | 
				
			||||||
      assertTrue(0 <= cat.getPosition());
 | 
					      assertTrue(0 <= cat.getPosition());
 | 
				
			||||||
    } finally {
 | 
					    } finally {
 | 
				
			||||||
      c.close();
 | 
					      c.close();
 | 
				
			||||||
@@ -171,101 +167,6 @@ public class SchemaCreatorTest extends TestCase {
 | 
				
			|||||||
    assertValueRange(codeReview, -2, -1, 0, 1, 2);
 | 
					    assertValueRange(codeReview, -2, -1, 0, 1, 2);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public void testCreateSchema_ApprovalCategory_Read() throws OrmException {
 | 
					 | 
				
			||||||
    final ReviewDb c = db.create().open();
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      final ApprovalCategory cat;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      cat = c.approvalCategories().get(ApprovalCategory.READ);
 | 
					 | 
				
			||||||
      assertNotNull(cat);
 | 
					 | 
				
			||||||
      assertEquals(ApprovalCategory.READ, cat.getId());
 | 
					 | 
				
			||||||
      assertEquals("Read Access", cat.getName());
 | 
					 | 
				
			||||||
      assertNull(cat.getAbbreviatedName());
 | 
					 | 
				
			||||||
      assertEquals(NoOpFunction.NAME, cat.getFunctionName());
 | 
					 | 
				
			||||||
      assertTrue(cat.isAction());
 | 
					 | 
				
			||||||
    } finally {
 | 
					 | 
				
			||||||
      c.close();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    assertValueRange(ApprovalCategory.READ, -1, 1, 2, 3);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public void testCreateSchema_ApprovalCategory_Submit() throws OrmException {
 | 
					 | 
				
			||||||
    final ReviewDb c = db.create().open();
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      final ApprovalCategory cat;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      cat = c.approvalCategories().get(ApprovalCategory.SUBMIT);
 | 
					 | 
				
			||||||
      assertNotNull(cat);
 | 
					 | 
				
			||||||
      assertEquals(ApprovalCategory.SUBMIT, cat.getId());
 | 
					 | 
				
			||||||
      assertEquals("Submit", cat.getName());
 | 
					 | 
				
			||||||
      assertNull(cat.getAbbreviatedName());
 | 
					 | 
				
			||||||
      assertEquals(SubmitFunction.NAME, cat.getFunctionName());
 | 
					 | 
				
			||||||
      assertTrue(cat.isAction());
 | 
					 | 
				
			||||||
    } finally {
 | 
					 | 
				
			||||||
      c.close();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    assertValueRange(ApprovalCategory.SUBMIT, 1);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public void testCreateSchema_ApprovalCategory_PushTag() throws OrmException {
 | 
					 | 
				
			||||||
    final ReviewDb c = db.create().open();
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      final ApprovalCategory cat;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      cat = c.approvalCategories().get(ApprovalCategory.PUSH_TAG);
 | 
					 | 
				
			||||||
      assertNotNull(cat);
 | 
					 | 
				
			||||||
      assertEquals(ApprovalCategory.PUSH_TAG, cat.getId());
 | 
					 | 
				
			||||||
      assertEquals("Push Tag", cat.getName());
 | 
					 | 
				
			||||||
      assertNull(cat.getAbbreviatedName());
 | 
					 | 
				
			||||||
      assertEquals(NoOpFunction.NAME, cat.getFunctionName());
 | 
					 | 
				
			||||||
      assertTrue(cat.isAction());
 | 
					 | 
				
			||||||
    } finally {
 | 
					 | 
				
			||||||
      c.close();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    assertValueRange(ApprovalCategory.PUSH_TAG, //
 | 
					 | 
				
			||||||
        ApprovalCategory.PUSH_TAG_SIGNED, //
 | 
					 | 
				
			||||||
        ApprovalCategory.PUSH_TAG_ANNOTATED);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public void testCreateSchema_ApprovalCategory_PushHead() throws OrmException {
 | 
					 | 
				
			||||||
    final ReviewDb c = db.create().open();
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      final ApprovalCategory cat;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      cat = c.approvalCategories().get(ApprovalCategory.PUSH_HEAD);
 | 
					 | 
				
			||||||
      assertNotNull(cat);
 | 
					 | 
				
			||||||
      assertEquals(ApprovalCategory.PUSH_HEAD, cat.getId());
 | 
					 | 
				
			||||||
      assertEquals("Push Branch", cat.getName());
 | 
					 | 
				
			||||||
      assertNull(cat.getAbbreviatedName());
 | 
					 | 
				
			||||||
      assertEquals(NoOpFunction.NAME, cat.getFunctionName());
 | 
					 | 
				
			||||||
      assertTrue(cat.isAction());
 | 
					 | 
				
			||||||
    } finally {
 | 
					 | 
				
			||||||
      c.close();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    assertValueRange(ApprovalCategory.PUSH_HEAD, //
 | 
					 | 
				
			||||||
        ApprovalCategory.PUSH_HEAD_UPDATE, //
 | 
					 | 
				
			||||||
        ApprovalCategory.PUSH_HEAD_CREATE, //
 | 
					 | 
				
			||||||
        ApprovalCategory.PUSH_HEAD_REPLACE);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public void testCreateSchema_ApprovalCategory_Owner() throws OrmException {
 | 
					 | 
				
			||||||
    final ReviewDb c = db.create().open();
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      final ApprovalCategory cat;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      cat = c.approvalCategories().get(ApprovalCategory.OWN);
 | 
					 | 
				
			||||||
      assertNotNull(cat);
 | 
					 | 
				
			||||||
      assertEquals(ApprovalCategory.OWN, cat.getId());
 | 
					 | 
				
			||||||
      assertEquals("Owner", cat.getName());
 | 
					 | 
				
			||||||
      assertNull(cat.getAbbreviatedName());
 | 
					 | 
				
			||||||
      assertEquals(NoOpFunction.NAME, cat.getFunctionName());
 | 
					 | 
				
			||||||
      assertTrue(cat.isAction());
 | 
					 | 
				
			||||||
    } finally {
 | 
					 | 
				
			||||||
      c.close();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    assertValueRange(ApprovalCategory.OWN, 1);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private void assertValueRange(ApprovalCategory.Id cat, int... range)
 | 
					  private void assertValueRange(ApprovalCategory.Id cat, int... range)
 | 
				
			||||||
      throws OrmException {
 | 
					      throws OrmException {
 | 
				
			||||||
    final HashSet<ApprovalCategoryValue.Id> act =
 | 
					    final HashSet<ApprovalCategoryValue.Id> act =
 | 
				
			||||||
@@ -295,55 +196,4 @@ public class SchemaCreatorTest extends TestCase {
 | 
				
			|||||||
      fail("Category " + cat + " has additional values: " + act);
 | 
					      fail("Category " + cat + " has additional values: " + act);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					 | 
				
			||||||
  public void testCreateSchema_DefaultAccess_AnonymousUsers()
 | 
					 | 
				
			||||||
      throws OrmException {
 | 
					 | 
				
			||||||
    db.create();
 | 
					 | 
				
			||||||
    final SystemConfig config = db.getSystemConfig();
 | 
					 | 
				
			||||||
    assertDefaultRight("refs/*", config.anonymousGroupId,
 | 
					 | 
				
			||||||
        ApprovalCategory.READ, 1, 1);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public void testCreateSchema_DefaultAccess_RegisteredUsers()
 | 
					 | 
				
			||||||
      throws OrmException {
 | 
					 | 
				
			||||||
    db.create();
 | 
					 | 
				
			||||||
    final SystemConfig config = db.getSystemConfig();
 | 
					 | 
				
			||||||
    assertDefaultRight("refs/*", config.registeredGroupId,
 | 
					 | 
				
			||||||
        ApprovalCategory.READ, 1, 2);
 | 
					 | 
				
			||||||
    assertDefaultRight("refs/heads/*", config.registeredGroupId, codeReview,
 | 
					 | 
				
			||||||
        -1, 1);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  public void testCreateSchema_DefaultAccess_Administrators()
 | 
					 | 
				
			||||||
      throws OrmException {
 | 
					 | 
				
			||||||
    db.create();
 | 
					 | 
				
			||||||
    final SystemConfig config = db.getSystemConfig();
 | 
					 | 
				
			||||||
    assertDefaultRight("refs/*", config.adminGroupId, ApprovalCategory.READ, 1,
 | 
					 | 
				
			||||||
        1);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  private void assertDefaultRight(final String pattern,
 | 
					 | 
				
			||||||
      final AccountGroup.Id group, final ApprovalCategory.Id category, int min,
 | 
					 | 
				
			||||||
      int max) throws OrmException {
 | 
					 | 
				
			||||||
    final ReviewDb c = db.open();
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      final SystemConfig cfg;
 | 
					 | 
				
			||||||
      final RefRight right;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      cfg = c.systemConfig().get(new SystemConfig.Key());
 | 
					 | 
				
			||||||
      right =
 | 
					 | 
				
			||||||
          c.refRights().get(
 | 
					 | 
				
			||||||
              new RefRight.Key(cfg.wildProjectName, new RefRight.RefPattern(
 | 
					 | 
				
			||||||
                  pattern), category, group));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      assertNotNull(right);
 | 
					 | 
				
			||||||
      assertEquals(cfg.wildProjectName, right.getProjectNameKey());
 | 
					 | 
				
			||||||
      assertEquals(group, right.getAccountGroupId());
 | 
					 | 
				
			||||||
      assertEquals(category, right.getApprovalCategoryId());
 | 
					 | 
				
			||||||
      assertEquals(min, right.getMinValue());
 | 
					 | 
				
			||||||
      assertEquals(max, right.getMaxValue());
 | 
					 | 
				
			||||||
    } finally {
 | 
					 | 
				
			||||||
      c.close();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -135,10 +135,6 @@ final class AdminSetParent extends BaseCommand {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Invalidate all projects in cache since inherited rights were changed.
 | 
					 | 
				
			||||||
    //
 | 
					 | 
				
			||||||
    projectCache.evictAll();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (err.length() > 0) {
 | 
					    if (err.length() > 0) {
 | 
				
			||||||
      while (err.charAt(err.length() - 1) == '\n') {
 | 
					      while (err.charAt(err.length() - 1) == '\n') {
 | 
				
			||||||
        err.setLength(err.length() - 1);
 | 
					        err.setLength(err.length() - 1);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,11 +15,12 @@
 | 
				
			|||||||
package com.google.gerrit.sshd.commands;
 | 
					package com.google.gerrit.sshd.commands;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.google.gerrit.common.CollectionsUtil;
 | 
					import com.google.gerrit.common.CollectionsUtil;
 | 
				
			||||||
 | 
					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.AccountGroup;
 | 
				
			||||||
import com.google.gerrit.reviewdb.ApprovalCategory;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.Project;
 | 
					import com.google.gerrit.reviewdb.Project;
 | 
				
			||||||
import com.google.gerrit.reviewdb.RefRight;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.ReviewDb;
 | 
					 | 
				
			||||||
import com.google.gerrit.reviewdb.Project.SubmitType;
 | 
					import com.google.gerrit.reviewdb.Project.SubmitType;
 | 
				
			||||||
import com.google.gerrit.server.GerritPersonIdent;
 | 
					import com.google.gerrit.server.GerritPersonIdent;
 | 
				
			||||||
import com.google.gerrit.server.IdentifiedUser;
 | 
					import com.google.gerrit.server.IdentifiedUser;
 | 
				
			||||||
@@ -33,7 +34,6 @@ import com.google.gerrit.server.git.ReplicationQueue;
 | 
				
			|||||||
import com.google.gerrit.server.project.ProjectCache;
 | 
					import com.google.gerrit.server.project.ProjectCache;
 | 
				
			||||||
import com.google.gerrit.server.project.ProjectControl;
 | 
					import com.google.gerrit.server.project.ProjectControl;
 | 
				
			||||||
import com.google.gerrit.sshd.BaseCommand;
 | 
					import com.google.gerrit.sshd.BaseCommand;
 | 
				
			||||||
import com.google.gwtorm.client.OrmException;
 | 
					 | 
				
			||||||
import com.google.inject.Inject;
 | 
					import com.google.inject.Inject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import org.apache.sshd.server.Environment;
 | 
					import org.apache.sshd.server.Environment;
 | 
				
			||||||
@@ -96,9 +96,6 @@ final class CreateProject extends BaseCommand {
 | 
				
			|||||||
  @Option(name = "--empty-commit", usage = "to create initial empty commit")
 | 
					  @Option(name = "--empty-commit", usage = "to create initial empty commit")
 | 
				
			||||||
  private boolean createEmptyCommit;
 | 
					  private boolean createEmptyCommit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @Inject
 | 
					 | 
				
			||||||
  private ReviewDb db;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  @Inject
 | 
					  @Inject
 | 
				
			||||||
  private GitRepositoryManager repoManager;
 | 
					  private GitRepositoryManager repoManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -202,27 +199,11 @@ final class CreateProject extends BaseCommand {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  private void createProject() throws OrmException, IOException,
 | 
					  private void createProject() throws IOException, ConfigInvalidException {
 | 
				
			||||||
      ConfigInvalidException {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    List<RefRight> access = new ArrayList<RefRight>();
 | 
					 | 
				
			||||||
    for (AccountGroup.UUID ownerId : ownerIds) {
 | 
					 | 
				
			||||||
      AccountGroup group = groupCache.get(ownerId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      final RefRight.Key prk =
 | 
					 | 
				
			||||||
          new RefRight.Key(nameKey, new RefRight.RefPattern(
 | 
					 | 
				
			||||||
              RefRight.ALL), ApprovalCategory.OWN, group.getId());
 | 
					 | 
				
			||||||
      final RefRight pr = new RefRight(prk);
 | 
					 | 
				
			||||||
      pr.setMaxValue((short) 1);
 | 
					 | 
				
			||||||
      pr.setMinValue((short) 1);
 | 
					 | 
				
			||||||
      access.add(pr);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    db.refRights().insert(access);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    Project.NameKey nameKey = new Project.NameKey(projectName);
 | 
					 | 
				
			||||||
    MetaDataUpdate md = metaDataUpdateFactory.create(nameKey);
 | 
					    MetaDataUpdate md = metaDataUpdateFactory.create(nameKey);
 | 
				
			||||||
    try {
 | 
					    try {
 | 
				
			||||||
      ProjectConfig config = ProjectConfig.read(md);
 | 
					      ProjectConfig config = ProjectConfig.read(md);
 | 
				
			||||||
 | 
					      config.load(md);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      Project newProject = config.getProject();
 | 
					      Project newProject = config.getProject();
 | 
				
			||||||
      newProject.setDescription(projectDescription);
 | 
					      newProject.setDescription(projectDescription);
 | 
				
			||||||
@@ -235,6 +216,16 @@ final class CreateProject extends BaseCommand {
 | 
				
			|||||||
        newProject.setParentName(newParent.getProject().getName());
 | 
					        newProject.setParentName(newParent.getProject().getName());
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (!ownerIds.isEmpty()) {
 | 
				
			||||||
 | 
					        AccessSection all = config.getAccessSection(AccessSection.ALL, true);
 | 
				
			||||||
 | 
					        for (AccountGroup.UUID ownerId : ownerIds) {
 | 
				
			||||||
 | 
					          AccountGroup accountGroup = groupCache.get(ownerId);
 | 
				
			||||||
 | 
					          GroupReference group = config.resolve(accountGroup);
 | 
				
			||||||
 | 
					          all.getPermission(Permission.OWNER, true).add(
 | 
				
			||||||
 | 
					              new PermissionRule(group));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      md.setMessage("Created project\n");
 | 
					      md.setMessage("Created project\n");
 | 
				
			||||||
      if (!config.commit(md)) {
 | 
					      if (!config.commit(md)) {
 | 
				
			||||||
        throw new IOException("Cannot create " + projectName);
 | 
					        throw new IOException("Cannot create " + projectName);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -331,7 +331,7 @@ public class ReviewCommand extends BaseCommand {
 | 
				
			|||||||
        functionStateFactory.create(changeControl.getChange(), patchSetId,
 | 
					        functionStateFactory.create(changeControl.getChange(), patchSetId,
 | 
				
			||||||
            Collections.<PatchSetApproval> emptyList());
 | 
					            Collections.<PatchSetApproval> emptyList());
 | 
				
			||||||
    psa.setValue(v);
 | 
					    psa.setValue(v);
 | 
				
			||||||
    fs.normalize(approvalTypes.getApprovalType(psa.getCategoryId()), psa);
 | 
					    fs.normalize(approvalTypes.byId(psa.getCategoryId()), psa);
 | 
				
			||||||
    if (v != psa.getValue()) {
 | 
					    if (v != psa.getValue()) {
 | 
				
			||||||
      throw error(ao.name() + "=" + ao.value() + " not permitted");
 | 
					      throw error(ao.name() + "=" + ao.value() + " not permitted");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user