summaryrefslogtreecommitdiffstats
path: root/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
diff options
context:
space:
mode:
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java')
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java619
1 files changed, 218 insertions, 401 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
index 8ddf5850d0..db370e0727 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
@@ -14,34 +14,21 @@
package com.google.gerrit.server.project;
-import static com.google.gerrit.reviewdb.ApprovalCategory.FORGE_AUTHOR;
-import static com.google.gerrit.reviewdb.ApprovalCategory.FORGE_COMMITTER;
-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.reviewdb.AccountGroup;
-import com.google.gerrit.reviewdb.ApprovalCategory;
-import com.google.gerrit.reviewdb.RefRight;
-import com.google.gerrit.reviewdb.SystemConfig;
+import com.google.gerrit.common.data.AccessSection;
+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.common.data.RefConfigSection;
+import com.google.gerrit.common.errors.InvalidNameException;
+import com.google.gerrit.reviewdb.client.Project;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.IdentifiedUser;
-import com.google.inject.Inject;
-import com.google.inject.assistedinject.Assisted;
+import com.google.gerrit.server.git.GitRepositoryManager;
import dk.brics.automaton.RegExp;
-import org.apache.commons.lang.StringUtils;
import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevObject;
import org.eclipse.jgit.revwalk.RevTag;
@@ -49,47 +36,33 @@ import org.eclipse.jgit.revwalk.RevWalk;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
-import java.util.regex.Pattern;
/** Manages access control for Git references (aka branches, tags). */
public class RefControl {
- public interface Factory {
- RefControl create(ProjectControl projectControl, String ref);
- }
-
- private final SystemConfig systemConfig;
private final ProjectControl projectControl;
private final String refName;
- private Boolean canForgeAuthor;
- private Boolean canForgeCommitter;
-
- @Inject
- protected RefControl(final SystemConfig systemConfig,
- @Assisted final ProjectControl projectControl,
- @Assisted String ref) {
- this.systemConfig = systemConfig;
- if (isRE(ref)) {
- ref = shortestExample(ref);
+ /** All permissions that apply to this reference. */
+ private final PermissionCollection relevant;
- } else if (ref.endsWith("/*")) {
- ref = ref.substring(0, ref.length() - 1);
+ /** Cached set of permissions matching this user. */
+ private final Map<String, List<PermissionRule>> effective;
- }
+ private Boolean owner;
+ private Boolean canForgeAuthor;
+ private Boolean canForgeCommitter;
+ RefControl(ProjectControl projectControl, String ref,
+ PermissionCollection relevant) {
this.projectControl = projectControl;
this.refName = ref;
+ this.relevant = relevant;
+ this.effective = new HashMap<String, List<PermissionRule>>();
}
public String getRefName() {
@@ -101,39 +74,35 @@ public class RefControl {
}
public CurrentUser getCurrentUser() {
- return getProjectControl().getCurrentUser();
- }
-
- public RefControl forAnonymousUser() {
- return getProjectControl().forAnonymousUser().controlForRef(getRefName());
+ return projectControl.getCurrentUser();
}
- public RefControl forUser(final CurrentUser who) {
- return getProjectControl().forUser(who).controlForRef(getRefName());
+ public RefControl forUser(CurrentUser who) {
+ ProjectControl newCtl = projectControl.forUser(who);
+ if (relevant.isUserSpecific()) {
+ return newCtl.controlForRef(getRefName());
+ } else {
+ return new RefControl(newCtl, getRefName(), relevant);
+ }
}
/** Is this user a ref owner? */
public boolean isOwner() {
- if (canPerform(OWN, (short) 1)) {
- return true;
- }
+ if (owner == null) {
+ if (canPerform(Permission.OWNER)) {
+ owner = true;
- // We have to prevent infinite recursion here, the project control
- // calls us to find out if there is ownership of all references in
- // order to determine project level ownership.
- //
- if (getRefName().equals(
- RefRight.ALL.substring(0, RefRight.ALL.length() - 1))) {
- return getCurrentUser().isAdministrator();
- } else {
- return getProjectControl().isOwner();
+ } else {
+ owner = projectControl.isOwner();
+ }
}
+ return owner;
}
/** Can this user see this reference exists? */
public boolean isVisible() {
- return getProjectControl().visibleForReplication()
- || canPerform(READ, (short) 1);
+ return (projectControl.visibleForReplication() || canPerform(Permission.READ))
+ && canRead();
}
/**
@@ -144,27 +113,88 @@ public class RefControl {
* ref
*/
public boolean canUpload() {
- return canPerform(READ, (short) 2);
+ return projectControl.controlForRef("refs/for/" + getRefName())
+ .canPerform(Permission.PUSH)
+ && canWrite();
}
/** @return true if this user can submit merge patch sets to this ref */
public boolean canUploadMerges() {
- return canPerform(READ, (short) 3);
+ return projectControl.controlForRef("refs/for/" + getRefName())
+ .canPerform(Permission.PUSH_MERGE)
+ && canWrite();
+ }
+
+ /** @return true if this user can rebase changes on this ref */
+ public boolean canRebase() {
+ return canPerform(Permission.REBASE)
+ && canWrite();
}
/** @return true if this user can submit patch sets to this ref */
public boolean canSubmit() {
- return canPerform(ApprovalCategory.SUBMIT, (short) 1);
+ if (GitRepositoryManager.REF_CONFIG.equals(refName)) {
+ // Always allow project owners to submit configuration changes.
+ // Submitting configuration changes modifies the access control
+ // rules. Allowing this to be done by a non-project-owner opens
+ // a security hole enabling editing of access rules, and thus
+ // granting of powers beyond submitting to the configuration.
+ return projectControl.isOwner();
+ }
+ return canPerform(Permission.SUBMIT)
+ && canWrite();
}
/** @return true if the user can update the reference as a fast-forward. */
public boolean canUpdate() {
- return canPerform(PUSH_HEAD, PUSH_HEAD_UPDATE);
+ if (GitRepositoryManager.REF_CONFIG.equals(refName)
+ && !projectControl.isOwner()) {
+ // Pushing requires being at least project owner, in addition to push.
+ // Pushing configuration changes modifies the access control
+ // rules. Allowing this to be done by a non-project-owner opens
+ // a security hole enabling editing of access rules, and thus
+ // granting of powers beyond pushing to the configuration.
+ return false;
+ }
+ return canPerform(Permission.PUSH)
+ && canWrite();
}
/** @return true if the user can rewind (force push) the reference. */
public boolean canForceUpdate() {
- return canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE) || canDelete();
+ return (canPushWithForce() || canDelete()) && canWrite();
+ }
+
+ public boolean canWrite() {
+ return getProjectControl().getProject().getState().equals(
+ Project.State.ACTIVE);
+ }
+
+ public boolean canRead() {
+ return getProjectControl().getProject().getState().equals(
+ Project.State.READ_ONLY) || canWrite();
+ }
+
+ private boolean canPushWithForce() {
+ if (!canWrite() || (GitRepositoryManager.REF_CONFIG.equals(refName)
+ && !projectControl.isOwner())) {
+ // Pushing requires being at least project owner, in addition to push.
+ // Pushing configuration changes modifies the access control
+ // rules. Allowing this to be done by a non-project-owner opens
+ // a security hole enabling editing of access rules, and thus
+ // granting of powers beyond pushing to the configuration.
+ return false;
+ }
+ boolean result = false;
+ for (PermissionRule rule : access(Permission.PUSH)) {
+ if (rule.isBlock()) {
+ return false;
+ }
+ if (rule.getForce()) {
+ result = true;
+ }
+ }
+ return result;
}
/**
@@ -175,6 +205,9 @@ public class RefControl {
* @return {@code true} if the user specified can create a new Git ref
*/
public boolean canCreate(RevWalk rw, RevObject object) {
+ if (!canWrite()) {
+ return false;
+ }
boolean owner;
switch (getCurrentUser().getAccessPath()) {
case WEB_UI:
@@ -186,7 +219,7 @@ public class RefControl {
}
if (object instanceof RevCommit) {
- return owner || canPerform(PUSH_HEAD, PUSH_HEAD_CREATE);
+ return owner || canPerform(Permission.CREATE);
} else if (object instanceof RevTag) {
final RevTag tag = (RevTag) object;
@@ -208,7 +241,7 @@ public class RefControl {
} else {
valid = false;
}
- if (!valid && !owner && !canPerform(FORGE_IDENTITY, FORGE_COMMITTER)) {
+ if (!valid && !owner && !canForgeCommitter()) {
return false;
}
}
@@ -217,9 +250,9 @@ public class RefControl {
// than if it doesn't have a PGP signature.
//
if (tag.getFullMessage().contains("-----BEGIN PGP SIGNATURE-----\n")) {
- return owner || canPerform(PUSH_TAG, PUSH_TAG_SIGNED);
+ return owner || canPerform(Permission.PUSH_TAG);
} else {
- return owner || canPerform(PUSH_TAG, PUSH_TAG_ANNOTATED);
+ return owner || canPerform(Permission.PUSH_TAG);
}
} else {
@@ -234,12 +267,21 @@ public class RefControl {
* @return {@code true} if the user specified can delete a Git ref.
*/
public boolean canDelete() {
+ if (!canWrite() || (GitRepositoryManager.REF_CONFIG.equals(refName))) {
+ // Never allow removal of the refs/meta/config branch.
+ // Deleting the branch would destroy all Gerrit specific
+ // metadata about the project, including its access rules.
+ // If a project is to be removed from Gerrit, its repository
+ // should be removed first.
+ return false;
+ }
+
switch (getCurrentUser().getAccessPath()) {
case WEB_UI:
- return isOwner() || canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE);
+ return isOwner() || canPushWithForce();
case GIT:
- return canPerform(PUSH_HEAD, PUSH_HEAD_REPLACE);
+ return canPushWithForce();
default:
return false;
@@ -249,7 +291,7 @@ public class RefControl {
/** @return true if this user can forge the author line in a commit. */
public boolean canForgeAuthor() {
if (canForgeAuthor == null) {
- canForgeAuthor = canPerform(FORGE_IDENTITY, FORGE_AUTHOR);
+ canForgeAuthor = canPerform(Permission.FORGE_AUTHOR);
}
return canForgeAuthor;
}
@@ -257,361 +299,118 @@ public class RefControl {
/** @return true if this user can forge the committer line in a commit. */
public boolean canForgeCommitter() {
if (canForgeCommitter == null) {
- canForgeCommitter = canPerform(FORGE_IDENTITY, FORGE_COMMITTER);
+ canForgeCommitter = canPerform(Permission.FORGE_COMMITTER);
}
return canForgeCommitter;
}
/** @return true if this user can forge the server on the committer line. */
public boolean canForgeGerritServerIdentity() {
- return canPerform(FORGE_IDENTITY, FORGE_SERVER);
- }
-
- public short normalize(ApprovalCategory.Id category, short score) {
- short minAllowed = 0, maxAllowed = 0;
- for (RefRight r : getApplicableRights(category)) {
- if (getCurrentUser().getEffectiveGroups().contains(r.getAccountGroupId())) {
- minAllowed = (short) Math.min(minAllowed, r.getMinValue());
- maxAllowed = (short) Math.max(maxAllowed, r.getMaxValue());
+ return canPerform(Permission.FORGE_SERVER);
+ }
+
+ /** All value ranges of any allowed label permission. */
+ public List<PermissionRange> getLabelRanges() {
+ List<PermissionRange> r = new ArrayList<PermissionRange>();
+ for (Map.Entry<String, List<PermissionRule>> e : relevant.getDeclaredPermissions()) {
+ if (Permission.isLabel(e.getKey())) {
+ int min = 0;
+ int max = 0;
+ for (PermissionRule rule : e.getValue()) {
+ if (projectControl.match(rule)) {
+ min = Math.min(min, rule.getMin());
+ max = Math.max(max, rule.getMax());
+ }
+ }
+ if (min != 0 || max != 0) {
+ r.add(new PermissionRange(e.getKey(), min, max));
+ }
}
}
-
- if (score < minAllowed) {
- score = minAllowed;
- }
- if (score > maxAllowed) {
- score = maxAllowed;
- }
- return score;
+ return r;
}
- /**
- * Convenience holder class used to map a ref pattern to the list of
- * {@code RefRight}s that use it in the database.
- */
- public final static class RefRightsForPattern {
- private final List<RefRight> rights;
- private boolean containsExclusive;
-
- public RefRightsForPattern() {
- rights = new ArrayList<RefRight>();
- containsExclusive = false;
+ /** The range of permitted values associated with a label permission. */
+ public PermissionRange getRange(String permission) {
+ if (Permission.isLabel(permission)) {
+ return toRange(permission, access(permission));
}
-
- public void addRight(RefRight right) {
- rights.add(right);
- if (right.isExclusive()) {
- containsExclusive = true;
+ return null;
+ }
+
+ private static PermissionRange toRange(String permissionName,
+ List<PermissionRule> ruleList) {
+ int min = 0;
+ int max = 0;
+ int blockMin = Integer.MIN_VALUE;
+ int blockMax = Integer.MAX_VALUE;
+ for (PermissionRule rule : ruleList) {
+ if (rule.isBlock()) {
+ blockMin = Math.max(blockMin, rule.getMin());
+ blockMax = Math.min(blockMax, rule.getMax());
+ } else {
+ min = Math.min(min, rule.getMin());
+ max = Math.max(max, rule.getMax());
}
}
-
- public List<RefRight> getRights() {
- return Collections.unmodifiableList(rights);
+ if (blockMin > Integer.MIN_VALUE) {
+ min = Math.max(min, blockMin + 1);
}
-
- public boolean containsExclusive() {
- return containsExclusive;
+ if (blockMax < Integer.MAX_VALUE) {
+ max = Math.min(max, blockMax - 1);
}
-
- /**
- * Returns The max allowed value for this ref pattern for all specified
- * groups.
- *
- * @param groups The groups of the user
- * @return The allowed value for this ref for all the specified groups
- */
- private boolean allowedValueForRef(Set<AccountGroup.Id> groups, short level) {
- for (RefRight right : rights) {
- if (groups.contains(right.getAccountGroupId())
- && right.getMaxValue() >= level) {
- return true;
- }
- }
- return false;
- }
- }
-
- boolean canPerform(ApprovalCategory.Id actionId, short level) {
- final Set<AccountGroup.Id> groups = getCurrentUser().getEffectiveGroups();
-
- List<RefRight> allRights = new ArrayList<RefRight>();
- allRights.addAll(getAllRights(actionId));
-
- SortedMap<String, RefRightsForPattern> perPatternRights =
- sortedRightsByPattern(allRights);
-
- for (RefRightsForPattern right : perPatternRights.values()) {
- if (right.allowedValueForRef(groups, level)) {
- return true;
- }
- if (right.containsExclusive() && !actionId.equals(OWN)) {
- break;
- }
- }
- return false;
+ return new PermissionRange(permissionName, min, max);
}
- /**
- * 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) {
- String example;
- 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);
+ /** True if the user has this permission. Works only for non labels. */
+ boolean canPerform(String permissionName) {
+ List<PermissionRule> access = access(permissionName);
+ for (PermissionRule rule : access) {
+ if (rule.isBlock() && !rule.getForce()) {
+ return false;
}
- patternRights.addRight(actionRight);
}
- return rights;
- }
-
- private List<RefRight> getAllRights(ApprovalCategory.Id actionId) {
- final List<RefRight> allRefRights = filter(getProjectState().getAllRights(actionId, true));
- return resolveOwnerGroups(allRefRights);
+ return !access.isEmpty();
}
- /**
- * 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;
- }
+ /** Rules for the given permission, or the empty list. */
+ private List<PermissionRule> access(String permissionName) {
+ List<PermissionRule> rules = effective.get(permissionName);
+ if (rules != null) {
+ return rules;
}
- return Collections.unmodifiableList(applicable);
- }
- /**
- * Resolves all refRights which assign privileges to the 'Project Owners'
- * 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;
- }
+ rules = relevant.getPermission(permissionName);
- /**
- * 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 (refRight.getAccountGroupId().equals(systemConfig.ownerGroupId)) {
- for (final AccountGroup.Id ownerGroup : getProjectState().getAllOwners()) {
- if (!ownerGroup.equals(systemConfig.ownerGroupId)) {
- resolvedRefRights.add(new RefRight(refRight, ownerGroup));
- }
- }
- } else {
- resolvedRefRights.add(refRight);
+ if (rules.isEmpty()) {
+ effective.put(permissionName, rules);
+ return rules;
}
- 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);
+ if (rules.size() == 1) {
+ if (!projectControl.match(rules.get(0))) {
+ rules = Collections.emptyList();
}
+ effective.put(permissionName, rules);
+ return rules;
}
- return mine;
- }
-
- private ProjectState getProjectState() {
- return projectControl.getProjectState();
- }
-
- private boolean matches(String refPattern) {
- if (isTemplate(refPattern)) {
- ParamertizedString template = new ParamertizedString(refPattern);
- HashMap<String, String> p = new HashMap<String, String>();
-
- if (getCurrentUser() instanceof IdentifiedUser) {
- p.put("username", ((IdentifiedUser) getCurrentUser()).getUserName());
- } else {
- // Right now we only template the username. If not available
- // this rule cannot be matched at all.
- //
- return false;
- }
- if (isRE(refPattern)) {
- for (Map.Entry<String, String> ent : p.entrySet()) {
- ent.setValue(escape(ent.getValue()));
- }
+ List<PermissionRule> mine = new ArrayList<PermissionRule>(rules.size());
+ for (PermissionRule rule : rules) {
+ if (projectControl.match(rule)) {
+ mine.add(rule);
}
-
- refPattern = template.replace(p);
}
- if (isRE(refPattern)) {
- return Pattern.matches(refPattern, getRefName());
-
- } else if (refPattern.endsWith("/*")) {
- String prefix = refPattern.substring(0, refPattern.length() - 1);
- return getRefName().startsWith(prefix);
-
- } else {
- return getRefName().equals(refPattern);
+ if (mine.isEmpty()) {
+ mine = Collections.emptyList();
}
+ effective.put(permissionName, mine);
+ return mine;
}
- private static boolean isTemplate(String refPattern) {
- return 0 <= refPattern.indexOf("${");
- }
-
- private static String escape(String value) {
- // Right now the only special character allowed in a
- // variable value is a . in the username.
- //
- return value.replace(".", "\\.");
- }
-
- private static boolean isRE(String refPattern) {
- return refPattern.startsWith(RefRight.REGEX_PREFIX);
+ public static boolean isRE(String refPattern) {
+ return refPattern.startsWith(AccessSection.REGEX_PREFIX);
}
public static String shortestExample(String pattern) {
@@ -624,10 +423,28 @@ public class RefControl {
}
}
- private static RegExp toRegExp(String refPattern) {
+ public static RegExp toRegExp(String refPattern) {
if (isRE(refPattern)) {
refPattern = refPattern.substring(1);
}
return new RegExp(refPattern, RegExp.NONE);
}
+
+ public static void validateRefPattern(String refPattern)
+ throws InvalidNameException {
+ if (refPattern.startsWith(RefConfigSection.REGEX_PREFIX)) {
+ if (!Repository.isValidRefName(RefControl.shortestExample(refPattern))) {
+ throw new InvalidNameException(refPattern);
+ }
+ } else if (refPattern.equals(RefConfigSection.ALL)) {
+ // This is a special case we have to allow, it fails below.
+ } else if (refPattern.endsWith("/*")) {
+ String prefix = refPattern.substring(0, refPattern.length() - 2);
+ if (!Repository.isValidRefName(prefix)) {
+ throw new InvalidNameException(refPattern);
+ }
+ } else if (!Repository.isValidRefName(refPattern)) {
+ throw new InvalidNameException(refPattern);
+ }
+ }
}