query: Enable configurable result limit
Allow site administrators to configure the query limit for users
to be above the default hard-coded value of 500.  Administrators
can add a global block to All-Projects project.config file with
group(s) that should have different limits:
  [capability]
    queryLimit = +0..1000 group Automated Tools
    queryLimit = +0..100 group Registered Users
When applying a query limit to a user the largest value granted by any
of their groups is used.
This limit applies not only to the `gerrit query` command, but also
to the web UI results pagination size.
Bug: issue 934
Change-Id: I4e05448794de44bf51d3f0121358e456ab965c8b
Signed-off-by: Shawn O. Pearce <sop@google.com>
			
			
This commit is contained in:
		| @@ -24,6 +24,9 @@ import java.util.Set; | ||||
|  | ||||
| /** Portion of a {@link Project} describing access rules. */ | ||||
| public class AccessSection implements Comparable<AccessSection> { | ||||
|   /** Special name given to the global capabilities; not a valid reference. */ | ||||
|   public static final String GLOBAL_CAPABILITIES = "GLOBAL_CAPABILITIES"; | ||||
|  | ||||
|   /** Pattern that matches all references in a project. */ | ||||
|   public static final String ALL = "refs/*"; | ||||
|  | ||||
| @@ -38,22 +41,22 @@ public class AccessSection implements Comparable<AccessSection> { | ||||
|     return name.startsWith("refs/") || name.startsWith("^refs/"); | ||||
|   } | ||||
|  | ||||
|   protected String refPattern; | ||||
|   protected String name; | ||||
|   protected List<Permission> permissions; | ||||
|  | ||||
|   protected AccessSection() { | ||||
|   } | ||||
|  | ||||
|   public AccessSection(String refPattern) { | ||||
|     setRefPattern(refPattern); | ||||
|     setName(refPattern); | ||||
|   } | ||||
|  | ||||
|   public String getRefPattern() { | ||||
|     return refPattern; | ||||
|   public String getName() { | ||||
|     return name; | ||||
|   } | ||||
|  | ||||
|   public void setRefPattern(String refPattern) { | ||||
|     this.refPattern = refPattern; | ||||
|   public void setName(String name) { | ||||
|     this.name = name; | ||||
|   } | ||||
|  | ||||
|   public List<Permission> getPermissions() { | ||||
| @@ -127,14 +130,14 @@ public class AccessSection implements Comparable<AccessSection> { | ||||
|   } | ||||
|  | ||||
|   private String comparePattern() { | ||||
|     if (getRefPattern().startsWith(REGEX_PREFIX)) { | ||||
|       return getRefPattern().substring(REGEX_PREFIX.length()); | ||||
|     if (getName().startsWith(REGEX_PREFIX)) { | ||||
|       return getName().substring(REGEX_PREFIX.length()); | ||||
|     } | ||||
|     return getRefPattern(); | ||||
|     return getName(); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public String toString() { | ||||
|     return "AccessSection[" + getRefPattern() + "]"; | ||||
|     return "AccessSection[" + getName() + "]"; | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,55 @@ | ||||
| // 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.common.data; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| /** Server wide capabilities. Represented as {@link Permission} objects. */ | ||||
| public class GlobalCapability { | ||||
|   public static final String QUERY_LIMIT = "queryLimit"; | ||||
|  | ||||
|   private static final List<String> NAMES_LC; | ||||
|  | ||||
|   static { | ||||
|     NAMES_LC = new ArrayList<String>(); | ||||
|     NAMES_LC.add(QUERY_LIMIT.toLowerCase()); | ||||
|   } | ||||
|  | ||||
|   /** @return true if the name is recognized as a capability name. */ | ||||
|   public static boolean isCapability(String varName) { | ||||
|     return NAMES_LC.contains(varName.toLowerCase()); | ||||
|   } | ||||
|  | ||||
|   /** @return true if the capability should have a range attached. */ | ||||
|   public static boolean hasRange(String varName) { | ||||
|     return QUERY_LIMIT.equalsIgnoreCase(varName); | ||||
|   } | ||||
|  | ||||
|   /** @return the valid range for the capability if it has one, otherwise null. */ | ||||
|   public static PermissionRange.WithDefaults getRange(String varName) { | ||||
|     if (QUERY_LIMIT.equalsIgnoreCase(varName)) { | ||||
|       return new PermissionRange.WithDefaults( | ||||
|           varName, | ||||
|           0, Integer.MAX_VALUE, | ||||
|           0, 500); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   private GlobalCapability() { | ||||
|     // Utility class, do not create instances. | ||||
|   } | ||||
| } | ||||
| @@ -14,7 +14,50 @@ | ||||
|  | ||||
| package com.google.gerrit.common.data; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| public class PermissionRange implements Comparable<PermissionRange> { | ||||
|   public static class WithDefaults extends PermissionRange { | ||||
|     protected int defaultMin; | ||||
|     protected int defaultMax; | ||||
|  | ||||
|     protected WithDefaults() { | ||||
|     } | ||||
|  | ||||
|     public WithDefaults(String name, int min, int max, int defMin, int defMax) { | ||||
|       super(name, min, max); | ||||
|       setDefaultRange(defMin, defMax); | ||||
|     } | ||||
|  | ||||
|     public int getDefaultMin() { | ||||
|       return defaultMin; | ||||
|     } | ||||
|  | ||||
|     public int getDefaultMax() { | ||||
|       return defaultMax; | ||||
|     } | ||||
|  | ||||
|     public void setDefaultRange(int min, int max) { | ||||
|       defaultMin = min; | ||||
|       defaultMax = max; | ||||
|     } | ||||
|  | ||||
|     /** @return all values between {@link #getMin()} and {@link #getMax()} */ | ||||
|     public List<Integer> getValuesAsList() { | ||||
|       ArrayList<Integer> r = new ArrayList<Integer>(getRangeSize()); | ||||
|       for (int i = min; i <= max; i++) { | ||||
|         r.add(i); | ||||
|       } | ||||
|       return r; | ||||
|     } | ||||
|  | ||||
|     /** @return number of values between {@link #getMin()} and {@link #getMax()} */ | ||||
|     public int getRangeSize() { | ||||
|       return max - min; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   protected String name; | ||||
|   protected int min; | ||||
|   protected int max; | ||||
|   | ||||
| @@ -170,7 +170,7 @@ public class PermissionRule implements Comparable<PermissionRule> { | ||||
|       int sp = src.indexOf(' '); | ||||
|       String range = src.substring(0, sp); | ||||
|  | ||||
|       if (range.matches("^([+-]\\d+)\\.\\.([+-]\\d)$")) { | ||||
|       if (range.matches("^([+-]?\\d+)\\.\\.([+-]?\\d+)$")) { | ||||
|         int dotdot = range.indexOf(".."); | ||||
|         int min = parseInt(range.substring(0, dotdot)); | ||||
|         int max = parseInt(range.substring(dotdot + 2)); | ||||
|   | ||||
| @@ -52,8 +52,21 @@ public class ProjectAccess { | ||||
|     local = as; | ||||
|   } | ||||
|  | ||||
|   public AccessSection getLocal(String name) { | ||||
|     for (AccessSection s : local) { | ||||
|       if (s.getName().equals(name)) { | ||||
|         return s; | ||||
|       } | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   public boolean isOwnerOf(AccessSection section) { | ||||
|     return getOwnerOf().contains(section.getRefPattern()); | ||||
|     return isOwnerOf(section.getName()); | ||||
|   } | ||||
|  | ||||
|   public boolean isOwnerOf(String name) { | ||||
|     return ownerOf.contains(name); | ||||
|   } | ||||
|  | ||||
|   public Set<String> getOwnerOf() { | ||||
|   | ||||
| @@ -18,6 +18,10 @@ package com.google.gerrit.common.errors; | ||||
| public class InvalidQueryException extends Exception { | ||||
|   private static final long serialVersionUID = 1L; | ||||
|  | ||||
|   public InvalidQueryException(String message) { | ||||
|     super(message); | ||||
|   } | ||||
|  | ||||
|   public InvalidQueryException(String message, String query) { | ||||
|     super("Invalid query: " + query + "\n\n" + message); | ||||
|   } | ||||
|   | ||||
| @@ -55,7 +55,7 @@ public class AccessSectionEditor extends Composite implements | ||||
|   private static final Binder uiBinder = GWT.create(Binder.class); | ||||
|  | ||||
|   @UiField | ||||
|   ValueEditor<String> refPattern; | ||||
|   ValueEditor<String> name; | ||||
|  | ||||
|   @UiField | ||||
|   FlowPanel permissionContainer; | ||||
| @@ -78,6 +78,11 @@ public class AccessSectionEditor extends Composite implements | ||||
|   @UiField | ||||
|   DivElement deleted; | ||||
|  | ||||
|   @UiField | ||||
|   SpanElement sectionType; | ||||
|   @UiField | ||||
|   SpanElement sectionName; | ||||
|  | ||||
|   private final ProjectAccess projectAccess; | ||||
|   private AccessSection value; | ||||
|   private boolean editing; | ||||
| @@ -115,7 +120,18 @@ public class AccessSectionEditor extends Composite implements | ||||
|   @UiHandler("deleteSection") | ||||
|   void onDeleteSection(ClickEvent event) { | ||||
|     isDeleted = true; | ||||
|     deletedName.setInnerText(refPattern.getValue()); | ||||
|  | ||||
|     if (name.isVisible() && AccessSection.isAccessSection(name.getValue())){ | ||||
|       deletedName.setInnerText(Util.M.deletedReference(name.getValue())); | ||||
|  | ||||
|     } else { | ||||
|       String name = Util.C.sectionNames().get(value.getName()); | ||||
|       if (name == null) { | ||||
|         name = value.getName(); | ||||
|       } | ||||
|       deletedName.setInnerText(Util.M.deletedSection(name)); | ||||
|     } | ||||
|  | ||||
|     normal.getStyle().setDisplay(Display.NONE); | ||||
|     deleted.getStyle().setDisplay(Display.BLOCK); | ||||
|   } | ||||
| @@ -134,11 +150,11 @@ public class AccessSectionEditor extends Composite implements | ||||
|   } | ||||
|  | ||||
|   void editRefPattern() { | ||||
|     refPattern.edit(); | ||||
|     name.edit(); | ||||
|     Scheduler.get().scheduleDeferred(new ScheduledCommand() { | ||||
|       @Override | ||||
|       public void execute() { | ||||
|         refPattern.setFocus(true); | ||||
|         name.setFocus(true); | ||||
|       }}); | ||||
|   } | ||||
|  | ||||
| @@ -157,9 +173,29 @@ public class AccessSectionEditor extends Composite implements | ||||
|     this.value = value; | ||||
|     this.readOnly = !editing || !projectAccess.isOwnerOf(value); | ||||
|  | ||||
|     refPattern.setEnabled(!readOnly); | ||||
|     name.setEnabled(!readOnly); | ||||
|     deleteSection.setVisible(!readOnly); | ||||
|  | ||||
|     if (AccessSection.isAccessSection(value.getName())) { | ||||
|       name.setVisible(true); | ||||
|       name.setIgnoreEditorValue(false); | ||||
|       sectionType.setInnerText(Util.C.sectionTypeReference()); | ||||
|  | ||||
|     } else { | ||||
|       name.setVisible(false); | ||||
|       name.setIgnoreEditorValue(true); | ||||
|  | ||||
|       String name = Util.C.sectionNames().get(value.getName()); | ||||
|       if (name != null) { | ||||
|         sectionType.setInnerText(name); | ||||
|         sectionName.getStyle().setDisplay(Display.NONE); | ||||
|       } else { | ||||
|         sectionType.setInnerText(Util.C.sectionTypeSection()); | ||||
|         sectionName.setInnerText(value.getName()); | ||||
|         sectionName.getStyle().clearDisplay(); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (readOnly) { | ||||
|       addContainer.getStyle().setDisplay(Display.NONE); | ||||
|     } else { | ||||
| @@ -173,16 +209,24 @@ public class AccessSectionEditor extends Composite implements | ||||
|  | ||||
|   private void rebuildPermissionSelector() { | ||||
|     List<String> perms = new ArrayList<String>(); | ||||
|     for (ApprovalType t : Gerrit.getConfig().getApprovalTypes() | ||||
|         .getApprovalTypes()) { | ||||
|       String varName = Permission.LABEL + t.getCategory().getLabelName(); | ||||
|       if (value.getPermission(varName) == null) { | ||||
|         perms.add(varName); | ||||
|  | ||||
|     if (AccessSection.GLOBAL_CAPABILITIES.equals(value.getName())) { | ||||
|       for (String varName : Util.C.capabilityNames().keySet()) { | ||||
|         if (value.getPermission(varName) == null) { | ||||
|           perms.add(varName); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     for (String varName : Util.C.permissionNames().keySet()) { | ||||
|       if (value.getPermission(varName) == null) { | ||||
|         perms.add(varName); | ||||
|     } else if (AccessSection.isAccessSection(value.getName())) { | ||||
|       for (ApprovalType t : Gerrit.getConfig().getApprovalTypes().getApprovalTypes()) { | ||||
|         String varName = Permission.LABEL + t.getCategory().getLabelName(); | ||||
|         if (value.getPermission(varName) == null) { | ||||
|           perms.add(varName); | ||||
|         } | ||||
|       } | ||||
|       for (String varName : Util.C.permissionNames().keySet()) { | ||||
|         if (value.getPermission(varName) == null) { | ||||
|           perms.add(varName); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     if (perms.isEmpty()) { | ||||
|   | ||||
| @@ -66,10 +66,10 @@ limitations under the License. | ||||
|     background-color: selectionColor; | ||||
|   } | ||||
|  | ||||
|   .refName { | ||||
|   .name { | ||||
|     width: 100%; | ||||
|   } | ||||
|   .refNameEdit { | ||||
|   .nameEdit { | ||||
|     width: 100%; | ||||
|   } | ||||
|  | ||||
| @@ -107,17 +107,20 @@ limitations under the License. | ||||
| <div ui:field='normal' class='{style.normal} {style.content}'> | ||||
|   <div class='{style.header}'> | ||||
|     <table class='{style.headerTable}'><tr> | ||||
|       <td class='{style.headerText}'><ui:msg>Reference:</ui:msg></td> | ||||
|       <td class='{style.headerText}'> | ||||
|         <span ui:field='sectionType'/> | ||||
|       </td> | ||||
|       <td width='100%'> | ||||
|         <my:ValueEditor | ||||
|             ui:field='refPattern' | ||||
|             addStyleNames='{style.refName}' | ||||
|             ui:field='name' | ||||
|             addStyleNames='{style.name}' | ||||
|             editTitle='Edit reference pattern'> | ||||
|           <ui:attribute name='editTitle'/> | ||||
|           <my:editor> | ||||
|             <my:RefPatternBox styleName='{style.refNameEdit}'/> | ||||
|             <my:RefPatternBox styleName='{style.nameEdit}'/> | ||||
|           </my:editor> | ||||
|         </my:ValueEditor> | ||||
|         <span ui:field='sectionName' class='{style.name}'/> | ||||
|       </td> | ||||
|     </tr></table> | ||||
|  | ||||
| @@ -144,7 +147,7 @@ limitations under the License. | ||||
|     ui:field='deleted' | ||||
|     class='{style.deleted} {res.css.deleted}' | ||||
|     style='display: none'> | ||||
|   <ui:msg>Reference <span ui:field='deletedName'/> was deleted</ui:msg> | ||||
|   <span ui:field='deletedName'/> | ||||
|   <g:Anchor | ||||
|       ui:field='undoDelete' | ||||
|       href='javascript:void' | ||||
|   | ||||
| @@ -113,4 +113,10 @@ public interface AdminConstants extends Constants { | ||||
|   String refErrorNoSpace(); | ||||
|   String refErrorPrintable(); | ||||
|   String errorsMustBeFixed(); | ||||
|  | ||||
|   Map<String, String> capabilityNames(); | ||||
|  | ||||
|   String sectionTypeReference(); | ||||
|   String sectionTypeSection(); | ||||
|   Map<String, String> sectionNames(); | ||||
| } | ||||
|   | ||||
| @@ -116,3 +116,15 @@ refErrorDoubleSlash = References cannot contain '//' | ||||
| refErrorNoSpace = References cannot contain spaces | ||||
| refErrorPrintable = References may contain only printable characters | ||||
| errorsMustBeFixed = Errors must be fixed before committing changes. | ||||
|  | ||||
| # Capability Names | ||||
| capabilityNames = \ | ||||
|   queryLimit | ||||
| queryLimit = Query Limit | ||||
|  | ||||
| # Section Names | ||||
| sectionTypeReference = Reference: | ||||
| sectionTypeSection = Section: | ||||
| sectionNames = \ | ||||
|   GLOBAL_CAPABILITIES | ||||
| GLOBAL_CAPABILITIES = Global Capabilities | ||||
|   | ||||
| @@ -21,4 +21,7 @@ public interface AdminMessages extends Messages { | ||||
|   String label(String name); | ||||
|   String project(String name); | ||||
|   String deletedGroup(int id); | ||||
|  | ||||
|   String deletedReference(String name); | ||||
|   String deletedSection(String name); | ||||
| } | ||||
|   | ||||
| @@ -2,3 +2,5 @@ group = Group {0} | ||||
| label = Label {0} | ||||
| project = Project {0} | ||||
| deletedGroup = Deleted Group {0} | ||||
| deletedReference = Reference {0} was deleted | ||||
| deletedSection = Section {0} was deleted | ||||
|   | ||||
| @@ -19,8 +19,10 @@ import com.google.gerrit.client.rpc.GerritCallback; | ||||
| import com.google.gerrit.client.ui.SuggestUtil; | ||||
| import com.google.gerrit.common.data.AccessSection; | ||||
| import com.google.gerrit.common.data.ApprovalType; | ||||
| import com.google.gerrit.common.data.GlobalCapability; | ||||
| import com.google.gerrit.common.data.GroupReference; | ||||
| import com.google.gerrit.common.data.Permission; | ||||
| import com.google.gerrit.common.data.PermissionRange; | ||||
| import com.google.gerrit.common.data.PermissionRule; | ||||
| import com.google.gwt.core.client.GWT; | ||||
| import com.google.gwt.core.client.Scheduler; | ||||
| @@ -98,7 +100,7 @@ public class PermissionEditor extends Composite implements Editor<Permission>, | ||||
|   private final boolean readOnly; | ||||
|   private final AccessSection section; | ||||
|   private Permission value; | ||||
|   private ApprovalType rangeType; | ||||
|   private PermissionRange.WithDefaults validRange; | ||||
|   private boolean isDeleted; | ||||
|  | ||||
|   public PermissionEditor(boolean readOnly, AccessSection section) { | ||||
| @@ -196,9 +198,9 @@ public class PermissionEditor extends Composite implements Editor<Permission>, | ||||
|     if (ref.getUUID() != null) { | ||||
|       if (value.getRule(ref) == null) { | ||||
|         PermissionRule newRule = value.getRule(ref, true); | ||||
|         if (rangeType != null) { | ||||
|           int min = rangeType.getMin().getValue(); | ||||
|           int max = rangeType.getMax().getValue(); | ||||
|         if (validRange != null) { | ||||
|           int min = validRange.getDefaultMin(); | ||||
|           int max = validRange.getDefaultMax(); | ||||
|           newRule.setRange(min, max); | ||||
|         } | ||||
|         rules.getList().add(newRule); | ||||
| @@ -238,11 +240,20 @@ public class PermissionEditor extends Composite implements Editor<Permission>, | ||||
|   @Override | ||||
|   public void setValue(Permission value) { | ||||
|     this.value = value; | ||||
|  | ||||
|     if (value.isLabel()) { | ||||
|       rangeType = | ||||
|           Gerrit.getConfig().getApprovalTypes().byLabel(value.getLabel()); | ||||
|       ApprovalType at = Gerrit.getConfig().getApprovalTypes().byLabel(value.getLabel()); | ||||
|       if (at != null) { | ||||
|         validRange = new PermissionRange.WithDefaults( | ||||
|             value.getName(), | ||||
|             at.getMin().getValue(), at.getMax().getValue(), | ||||
|             at.getMin().getValue(), at.getMax().getValue()); | ||||
|       } | ||||
|     } else if (GlobalCapability.isCapability(value.getName())) { | ||||
|       validRange = GlobalCapability.getRange(value.getName()); | ||||
|  | ||||
|     } else { | ||||
|       rangeType = null; | ||||
|       validRange = null; | ||||
|     } | ||||
|  | ||||
|     if (value != null && Permission.OWNER.equals(value.getName())) { | ||||
| @@ -279,7 +290,7 @@ public class PermissionEditor extends Composite implements Editor<Permission>, | ||||
|     @Override | ||||
|     public PermissionRuleEditor create(int index) { | ||||
|       PermissionRuleEditor subEditor = | ||||
|           new PermissionRuleEditor(readOnly, section, value, rangeType); | ||||
|           new PermissionRuleEditor(readOnly, section, value, validRange); | ||||
|       ruleContainer.insert(subEditor, index); | ||||
|       return subEditor; | ||||
|     } | ||||
|   | ||||
| @@ -24,7 +24,19 @@ import java.util.Map; | ||||
| class PermissionNameRenderer implements Renderer<String> { | ||||
|   static final PermissionNameRenderer INSTANCE = new PermissionNameRenderer(); | ||||
|  | ||||
|   private static Map<String, String> LC; | ||||
|   private static final Map<String, String> all; | ||||
|  | ||||
|   static { | ||||
|     all = new HashMap<String, String>(); | ||||
|     for (Map.Entry<String, String> e : Util.C.capabilityNames().entrySet()) { | ||||
|       all.put(e.getKey(), e.getValue()); | ||||
|       all.put(e.getKey().toLowerCase(), e.getValue()); | ||||
|     } | ||||
|     for (Map.Entry<String, String> e : Util.C.permissionNames().entrySet()) { | ||||
|       all.put(e.getKey(), e.getValue()); | ||||
|       all.put(e.getKey().toLowerCase(), e.getValue()); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public String render(String varName) { | ||||
| @@ -32,16 +44,9 @@ class PermissionNameRenderer implements Renderer<String> { | ||||
|       return Util.M.label(new Permission(varName).getLabel()); | ||||
|     } | ||||
|  | ||||
|     Map<String, String> m = Util.C.permissionNames(); | ||||
|     String desc = m.get(varName); | ||||
|     String desc = all.get(varName); | ||||
|     if (desc == null) { | ||||
|       if (LC == null) { | ||||
|         LC = new HashMap<String, String>(); | ||||
|         for (Map.Entry<String, String> e : m.entrySet()) { | ||||
|           LC.put(e.getKey().toLowerCase(), e.getValue()); | ||||
|         } | ||||
|       } | ||||
|       desc = LC.get(varName.toLowerCase()); | ||||
|       desc = all.get(varName.toLowerCase()); | ||||
|     } | ||||
|     return desc != null ? desc : varName; | ||||
|   } | ||||
|   | ||||
| @@ -20,9 +20,9 @@ import static com.google.gerrit.common.data.Permission.PUSH_TAG; | ||||
| import com.google.gerrit.client.Dispatcher; | ||||
| import com.google.gerrit.client.ui.Hyperlink; | ||||
| import com.google.gerrit.common.data.AccessSection; | ||||
| import com.google.gerrit.common.data.ApprovalType; | ||||
| import com.google.gerrit.common.data.GroupReference; | ||||
| import com.google.gerrit.common.data.Permission; | ||||
| import com.google.gerrit.common.data.PermissionRange; | ||||
| import com.google.gerrit.common.data.PermissionRule; | ||||
| import com.google.gwt.core.client.GWT; | ||||
| import com.google.gwt.dom.client.DivElement; | ||||
| @@ -30,7 +30,9 @@ import com.google.gwt.dom.client.SpanElement; | ||||
| import com.google.gwt.dom.client.Style.Display; | ||||
| import com.google.gwt.editor.client.Editor; | ||||
| import com.google.gwt.editor.client.EditorDelegate; | ||||
| import com.google.gwt.editor.client.IsEditor; | ||||
| import com.google.gwt.editor.client.ValueAwareEditor; | ||||
| import com.google.gwt.editor.client.adapters.TakesValueEditor; | ||||
| import com.google.gwt.event.dom.client.ClickEvent; | ||||
| import com.google.gwt.text.shared.Renderer; | ||||
| import com.google.gwt.uibinder.client.UiBinder; | ||||
| @@ -41,11 +43,13 @@ import com.google.gwt.user.client.ui.Anchor; | ||||
| import com.google.gwt.user.client.ui.CheckBox; | ||||
| import com.google.gwt.user.client.ui.Composite; | ||||
| import com.google.gwt.user.client.ui.HTMLPanel; | ||||
| import com.google.gwt.user.client.ui.IntegerBox; | ||||
| import com.google.gwt.user.client.ui.UIObject; | ||||
| import com.google.gwt.user.client.ui.ValueListBox; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
|  | ||||
| public class PermissionRuleEditor extends Composite implements | ||||
|     Editor<PermissionRule>, ValueAwareEditor<PermissionRule> { | ||||
| @@ -58,10 +62,10 @@ public class PermissionRuleEditor extends Composite implements | ||||
|   ValueListBox<PermissionRule.Action> action; | ||||
|  | ||||
|   @UiField(provided = true) | ||||
|   ValueListBox<Integer> min; | ||||
|   RangeBox min; | ||||
|  | ||||
|   @UiField(provided = true) | ||||
|   ValueListBox<Integer> max; | ||||
|   RangeBox max; | ||||
|  | ||||
|   @UiField | ||||
|   CheckBox force; | ||||
| @@ -87,18 +91,30 @@ public class PermissionRuleEditor extends Composite implements | ||||
|   private boolean isDeleted; | ||||
|  | ||||
|   public PermissionRuleEditor(boolean readOnly, AccessSection section, | ||||
|       Permission permission, ApprovalType labelRange) { | ||||
|       Permission permission, PermissionRange.WithDefaults validRange) { | ||||
|     action = new ValueListBox<PermissionRule.Action>(actionRenderer); | ||||
|     min = new ValueListBox<Integer>(rangeRenderer); | ||||
|     max = new ValueListBox<Integer>(rangeRenderer); | ||||
|  | ||||
|     if (labelRange != null){ | ||||
|       min.setValue((int) labelRange.getMin().getValue()); | ||||
|       max.setValue((int) labelRange.getMax().getValue()); | ||||
|     if (validRange != null && 10 < validRange.getRangeSize()) { | ||||
|         min = new RangeBox.Box(); | ||||
|         max = new RangeBox.Box(); | ||||
|  | ||||
|     } else if (validRange != null) { | ||||
|       RangeBox.List minList = new RangeBox.List(); | ||||
|       RangeBox.List maxList = new RangeBox.List(); | ||||
|       List<Integer> valueList = validRange.getValuesAsList(); | ||||
|  | ||||
|       minList.list.setValue(validRange.getMin()); | ||||
|       maxList.list.setValue(validRange.getMax()); | ||||
|  | ||||
|       minList.list.setAcceptableValues(valueList); | ||||
|       maxList.list.setAcceptableValues(valueList); | ||||
|  | ||||
|       min = minList; | ||||
|       max = maxList; | ||||
|  | ||||
|       min.setAcceptableValues(labelRange.getValuesAsList()); | ||||
|       max.setAcceptableValues(labelRange.getValuesAsList()); | ||||
|     } else { | ||||
|       min = new RangeBox.Box(); | ||||
|       max = new RangeBox.Box(); | ||||
|       action.setValue(PermissionRule.Action.ALLOW); | ||||
|       action.setAcceptableValues(Arrays.asList(PermissionRule.Action.values())); | ||||
|     } | ||||
| @@ -108,16 +124,17 @@ public class PermissionRuleEditor extends Composite implements | ||||
|     String name = permission.getName(); | ||||
|     boolean canForce = PUSH.equals(name) || PUSH_TAG.equals(name); | ||||
|     if (canForce) { | ||||
|       String ref = section.getRefPattern(); | ||||
|       String ref = section.getName(); | ||||
|       canForce = !ref.startsWith("refs/for/") && !ref.startsWith("^refs/for/"); | ||||
|     } | ||||
|     force.setVisible(canForce); | ||||
|     force.setEnabled(!readOnly); | ||||
|  | ||||
|     if (labelRange != null) { | ||||
|     if (validRange != null) { | ||||
|       min.setEnabled(!readOnly); | ||||
|       max.setEnabled(!readOnly); | ||||
|       action.getElement().getStyle().setDisplay(Display.NONE); | ||||
|       DOM.setElementPropertyBoolean(min.getElement(), "disabled", readOnly); | ||||
|       DOM.setElementPropertyBoolean(max.getElement(), "disabled", readOnly); | ||||
|  | ||||
|     } else { | ||||
|       rangeEditor.getStyle().setDisplay(Display.NONE); | ||||
|       DOM.setElementPropertyBoolean(action.getElement(), "disabled", readOnly); | ||||
| @@ -188,23 +205,5 @@ public class PermissionRuleEditor extends Composite implements | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private static class RangeRenderer implements Renderer<Integer> { | ||||
|     @Override | ||||
|     public String render(Integer object) { | ||||
|       if (0 <= object) { | ||||
|         return "+" + object; | ||||
|       } else { | ||||
|         return String.valueOf(object); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void render(Integer object, Appendable appendable) | ||||
|         throws IOException { | ||||
|       appendable.append(render(object)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private static final ActionRenderer actionRenderer = new ActionRenderer(); | ||||
|   private static final RangeRenderer rangeRenderer = new RangeRenderer(); | ||||
| } | ||||
|   | ||||
| @@ -74,8 +74,8 @@ limitations under the License. | ||||
| <div ui:field='normal' class='{style.normal}'> | ||||
|   <g:ValueListBox ui:field='action' styleName='{style.actionList}'/> | ||||
|   <span ui:field='rangeEditor'> | ||||
|     <g:ValueListBox ui:field='min' styleName='{style.minmax}'/> | ||||
|     <g:ValueListBox ui:field='max' styleName='{style.minmax}'/> | ||||
|     <g:Widget ui:field='min' styleName='{style.minmax}'/> | ||||
|     <g:Widget ui:field='max' styleName='{style.minmax}'/> | ||||
|   </span> | ||||
|  | ||||
|   <q:Hyperlink ui:field='groupNameLink' styleName='{style.groupName}'/> | ||||
|   | ||||
| @@ -80,6 +80,15 @@ public class ProjectAccessEditor extends Composite implements | ||||
|  | ||||
|   @Override | ||||
|   public void setValue(ProjectAccess value) { | ||||
|     // If the owner can edit the Global Capabilities but they don't exist in this | ||||
|     // project, create an empty one at the beginning of the list making it | ||||
|     // possible to add permissions to it. | ||||
|     if (editing | ||||
|         && value.isOwnerOf(AccessSection.GLOBAL_CAPABILITIES) | ||||
|         && value.getLocal(AccessSection.GLOBAL_CAPABILITIES) == null) { | ||||
|       value.getLocal().add(0, new AccessSection(AccessSection.GLOBAL_CAPABILITIES)); | ||||
|     } | ||||
|  | ||||
|     this.value = value; | ||||
|  | ||||
|     Project.NameKey parent = value.getInheritsFrom(); | ||||
| @@ -102,7 +111,7 @@ public class ProjectAccessEditor extends Composite implements | ||||
|  | ||||
|     for (int i = 0; i < src.size(); i++) { | ||||
|       AccessSectionEditor e = (AccessSectionEditor) localContainer.getWidget(i); | ||||
|       if (!e.isDeleted()) { | ||||
|       if (!e.isDeleted() && !src.get(i).getPermissions().isEmpty()) { | ||||
|         keep.add(src.get(i)); | ||||
|       } | ||||
|     } | ||||
|   | ||||
| @@ -18,6 +18,7 @@ import com.google.gerrit.client.Gerrit; | ||||
| import com.google.gerrit.client.rpc.GerritCallback; | ||||
| import com.google.gerrit.client.rpc.ScreenLoadCallback; | ||||
| import com.google.gerrit.common.PageLinks; | ||||
| import com.google.gerrit.common.data.AccessSection; | ||||
| import com.google.gerrit.common.data.ProjectAccess; | ||||
| import com.google.gerrit.reviewdb.Project; | ||||
| import com.google.gwt.core.client.GWT; | ||||
|   | ||||
| @@ -0,0 +1,90 @@ | ||||
| // 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.client.admin; | ||||
|  | ||||
| import com.google.gwt.editor.client.IsEditor; | ||||
| import com.google.gwt.editor.client.adapters.TakesValueEditor; | ||||
| import com.google.gwt.text.shared.Renderer; | ||||
| import com.google.gwt.user.client.DOM; | ||||
| import com.google.gwt.user.client.ui.Composite; | ||||
| import com.google.gwt.user.client.ui.IntegerBox; | ||||
| import com.google.gwt.user.client.ui.ValueListBox; | ||||
| import com.google.gwt.user.client.ui.ValueBoxBase.TextAlignment; | ||||
|  | ||||
| import java.io.IOException; | ||||
|  | ||||
| abstract class RangeBox extends Composite implements | ||||
|     IsEditor<TakesValueEditor<Integer>> { | ||||
|   static final RangeRenderer rangeRenderer = new RangeRenderer(); | ||||
|  | ||||
|   private static class RangeRenderer implements Renderer<Integer> { | ||||
|     @Override | ||||
|     public String render(Integer object) { | ||||
|       if (0 <= object) { | ||||
|         return "+" + object; | ||||
|       } else { | ||||
|         return String.valueOf(object); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void render(Integer object, Appendable appendable) | ||||
|         throws IOException { | ||||
|       appendable.append(render(object)); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static class List extends RangeBox { | ||||
|     final ValueListBox<Integer> list; | ||||
|  | ||||
|     List() { | ||||
|       list = new ValueListBox<Integer>(rangeRenderer); | ||||
|       initWidget(list); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     void setEnabled(boolean on) { | ||||
|       DOM.setElementPropertyBoolean(list.getElement(), "disabled", !on); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public TakesValueEditor<Integer> asEditor() { | ||||
|       return list.asEditor(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   static class Box extends RangeBox { | ||||
|     private final IntegerBox box; | ||||
|  | ||||
|     Box() { | ||||
|       box = new IntegerBox(); | ||||
|       box.setVisibleLength(10); | ||||
|       box.setAlignment(TextAlignment.RIGHT); | ||||
|       initWidget(box); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     void setEnabled(boolean on) { | ||||
|       DOM.setElementPropertyBoolean(box.getElement(), "disabled", !on); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public TakesValueEditor<Integer> asEditor() { | ||||
|       return box.asEditor(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   abstract void setEnabled(boolean on); | ||||
| } | ||||
| @@ -64,6 +64,8 @@ public class ValueEditor<T> extends Composite implements HasEditorErrors<T>, | ||||
|  | ||||
|   private ValueBoxBase<T> editChild; | ||||
|   private ValueBoxEditor<T> editProxy; | ||||
|   private boolean ignoreEditorValue; | ||||
|   private T value; | ||||
|  | ||||
|   public ValueEditor() { | ||||
|     startHandlers = new StartEditHandlers(); | ||||
| @@ -93,14 +95,19 @@ public class ValueEditor<T> extends Composite implements HasEditorErrors<T>, | ||||
|  | ||||
|   @Override | ||||
|   public T getValue() { | ||||
|     return asEditor().getValue(); | ||||
|     return ignoreEditorValue ? value : asEditor().getValue(); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void setValue(T value) { | ||||
|     this.value = value; | ||||
|     asEditor().setValue(value); | ||||
|   } | ||||
|  | ||||
|   void setIgnoreEditorValue(boolean off) { | ||||
|     ignoreEditorValue = off; | ||||
|   } | ||||
|  | ||||
|   public void setEditTitle(String title) { | ||||
|     editIcon.setTitle(title); | ||||
|   } | ||||
|   | ||||
| @@ -17,6 +17,8 @@ package com.google.gerrit.httpd.rpc; | ||||
| import com.google.gerrit.common.data.AccountDashboardInfo; | ||||
| import com.google.gerrit.common.data.ChangeInfo; | ||||
| import com.google.gerrit.common.data.ChangeListService; | ||||
| import com.google.gerrit.common.data.GlobalCapability; | ||||
| import com.google.gerrit.common.data.PermissionRange; | ||||
| import com.google.gerrit.common.data.SingleListChangeInfo; | ||||
| import com.google.gerrit.common.data.ToggleStarRequest; | ||||
| import com.google.gerrit.common.errors.InvalidQueryException; | ||||
| @@ -29,8 +31,10 @@ import com.google.gerrit.reviewdb.ReviewDb; | ||||
| import com.google.gerrit.reviewdb.StarredChange; | ||||
| import com.google.gerrit.server.CurrentUser; | ||||
| import com.google.gerrit.server.account.AccountInfoCacheFactory; | ||||
| import com.google.gerrit.server.account.CapabilityControl; | ||||
| import com.google.gerrit.server.project.ChangeControl; | ||||
| import com.google.gerrit.server.project.NoSuchChangeException; | ||||
| import com.google.gerrit.server.project.NoSuchProjectException; | ||||
| import com.google.gerrit.server.query.Predicate; | ||||
| import com.google.gerrit.server.query.QueryParseException; | ||||
| import com.google.gerrit.server.query.change.ChangeData; | ||||
| @@ -79,15 +83,10 @@ public class ChangeListServiceImpl extends BaseServiceImplementation implements | ||||
|         } | ||||
|       }; | ||||
|  | ||||
|   private static final int MAX_PER_PAGE = 100; | ||||
|  | ||||
|   private static int safePageSize(final int pageSize) { | ||||
|     return 0 < pageSize && pageSize <= MAX_PER_PAGE ? pageSize : MAX_PER_PAGE; | ||||
|   } | ||||
|  | ||||
|   private final Provider<CurrentUser> currentUser; | ||||
|   private final ChangeControl.Factory changeControlFactory; | ||||
|   private final AccountInfoCacheFactory.Factory accountInfoCacheFactory; | ||||
|   private final CapabilityControl.Factory capabilityControlFactory; | ||||
|  | ||||
|   private final ChangeQueryBuilder.Factory queryBuilder; | ||||
|   private final Provider<ChangeQueryRewriter> queryRewriter; | ||||
| @@ -97,12 +96,14 @@ public class ChangeListServiceImpl extends BaseServiceImplementation implements | ||||
|       final Provider<CurrentUser> currentUser, | ||||
|       final ChangeControl.Factory changeControlFactory, | ||||
|       final AccountInfoCacheFactory.Factory accountInfoCacheFactory, | ||||
|       final CapabilityControl.Factory capabilityControlFactory, | ||||
|       final ChangeQueryBuilder.Factory queryBuilder, | ||||
|       final Provider<ChangeQueryRewriter> queryRewriter) { | ||||
|     super(schema, currentUser); | ||||
|     this.currentUser = currentUser; | ||||
|     this.changeControlFactory = changeControlFactory; | ||||
|     this.accountInfoCacheFactory = accountInfoCacheFactory; | ||||
|     this.capabilityControlFactory = capabilityControlFactory; | ||||
|     this.queryBuilder = queryBuilder; | ||||
|     this.queryRewriter = queryRewriter; | ||||
|   } | ||||
| @@ -118,25 +119,33 @@ public class ChangeListServiceImpl extends BaseServiceImplementation implements | ||||
|   @Override | ||||
|   public void allQueryPrev(final String query, final String pos, | ||||
|       final int pageSize, final AsyncCallback<SingleListChangeInfo> callback) { | ||||
|     run(callback, new QueryPrev(pageSize, pos) { | ||||
|       @Override | ||||
|       ResultSet<Change> query(ReviewDb db, int lim, String key) | ||||
|           throws OrmException, InvalidQueryException { | ||||
|         return searchQuery(db, query, lim, key, QUERY_PREV); | ||||
|       } | ||||
|     }); | ||||
|     try { | ||||
|       run(callback, new QueryPrev(pageSize, pos) { | ||||
|         @Override | ||||
|         ResultSet<Change> query(ReviewDb db, int lim, String key) | ||||
|             throws OrmException, InvalidQueryException { | ||||
|           return searchQuery(db, query, lim, key, QUERY_PREV); | ||||
|         } | ||||
|       }); | ||||
|     } catch (InvalidQueryException e) { | ||||
|       callback.onFailure(e); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void allQueryNext(final String query, final String pos, | ||||
|       final int pageSize, final AsyncCallback<SingleListChangeInfo> callback) { | ||||
|     run(callback, new QueryNext(pageSize, pos) { | ||||
|       @Override | ||||
|       ResultSet<Change> query(ReviewDb db, int lim, String key) | ||||
|           throws OrmException, InvalidQueryException { | ||||
|         return searchQuery(db, query, lim, key, QUERY_NEXT); | ||||
|       } | ||||
|     }); | ||||
|     try { | ||||
|       run(callback, new QueryNext(pageSize, pos) { | ||||
|         @Override | ||||
|         ResultSet<Change> query(ReviewDb db, int lim, String key) | ||||
|             throws OrmException, InvalidQueryException { | ||||
|           return searchQuery(db, query, lim, key, QUERY_NEXT); | ||||
|         } | ||||
|       }); | ||||
|     } catch (InvalidQueryException e) { | ||||
|       callback.onFailure(e); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @SuppressWarnings("unchecked") | ||||
| @@ -290,6 +299,26 @@ public class ChangeListServiceImpl extends BaseServiceImplementation implements | ||||
|     callback.onSuccess(currentUser.get().getStarredChanges()); | ||||
|   } | ||||
|  | ||||
|   private int safePageSize(final int pageSize) throws InvalidQueryException { | ||||
|     int maxLimit; | ||||
|     try { | ||||
|       PermissionRange range = capabilityControlFactory.controlFor() | ||||
|           .getRange(GlobalCapability.QUERY_LIMIT); | ||||
|       if (range != null) { | ||||
|         maxLimit = range.getMax(); | ||||
|       } else { | ||||
|         maxLimit = GlobalCapability.getRange(GlobalCapability.QUERY_LIMIT) | ||||
|             .getDefaultMax(); | ||||
|       } | ||||
|     } catch (NoSuchProjectException noConfig) { | ||||
|       maxLimit = 0; | ||||
|     } | ||||
|     if (maxLimit == 0) { | ||||
|       throw new InvalidQueryException("Search Disabled"); | ||||
|     } | ||||
|     return 0 < pageSize && pageSize <= maxLimit ? pageSize : maxLimit; | ||||
|   } | ||||
|  | ||||
|   private List<ChangeInfo> filter(final ResultSet<Change> rs, | ||||
|       final Set<Change.Id> starred, final AccountInfoCacheFactory accts) { | ||||
|     final ArrayList<ChangeInfo> r = new ArrayList<ChangeInfo>(); | ||||
| @@ -309,7 +338,7 @@ public class ChangeListServiceImpl extends BaseServiceImplementation implements | ||||
|     protected final int limit; | ||||
|     protected final int slim; | ||||
|  | ||||
|     QueryNext(final int pageSize, final String pos) { | ||||
|     QueryNext(final int pageSize, final String pos) throws InvalidQueryException { | ||||
|       this.pos = pos; | ||||
|       this.limit = safePageSize(pageSize); | ||||
|       this.slim = limit + 1; | ||||
| @@ -353,7 +382,7 @@ public class ChangeListServiceImpl extends BaseServiceImplementation implements | ||||
|   } | ||||
|  | ||||
|   private abstract class QueryPrev extends QueryNext { | ||||
|     QueryPrev(int pageSize, String pos) { | ||||
|     QueryPrev(int pageSize, String pos) throws InvalidQueryException { | ||||
|       super(pageSize, pos); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -107,41 +107,48 @@ class ChangeProjectAccess extends Handler<ProjectAccess> { | ||||
|       Set<String> toDelete = scanSectionNames(config); | ||||
|  | ||||
|       for (AccessSection section : mergeSections(sectionList)) { | ||||
|         final String name = section.getRefPattern(); | ||||
|         if (!projectControl.controlForRef(name).isOwner()) { | ||||
|           continue; | ||||
|         } | ||||
|         String name = section.getName(); | ||||
|  | ||||
|         if (name.startsWith(AccessSection.REGEX_PREFIX)) { | ||||
|           if (!Repository.isValidRefName(RefControl.shortestExample(name))) { | ||||
|         if (AccessSection.GLOBAL_CAPABILITIES.equals(name)) { | ||||
|           if (!projectControl.isOwner()) { | ||||
|             continue; | ||||
|           } | ||||
|           replace(config, toDelete, section); | ||||
|  | ||||
|         } else if (AccessSection.isAccessSection(name)) { | ||||
|           if (!projectControl.controlForRef(name).isOwner()) { | ||||
|             continue; | ||||
|           } | ||||
|  | ||||
|           if (name.startsWith(AccessSection.REGEX_PREFIX)) { | ||||
|             if (!Repository.isValidRefName(RefControl.shortestExample(name))) { | ||||
|               throw new InvalidNameException(); | ||||
|             } | ||||
|  | ||||
|           } else if (name.equals(AccessSection.ALL)) { | ||||
|             // This is a special case we have to allow, it fails below. | ||||
|  | ||||
|           } else if (name.endsWith("/*")) { | ||||
|             String prefix = name.substring(0, name.length() - 2); | ||||
|             if (!Repository.isValidRefName(prefix)) { | ||||
|               throw new InvalidNameException(); | ||||
|             } | ||||
|  | ||||
|           } else if (!Repository.isValidRefName(name)) { | ||||
|             throw new InvalidNameException(); | ||||
|           } | ||||
|  | ||||
|         } else if (name.equals(AccessSection.ALL)) { | ||||
|           // This is a special case we have to allow, it fails below. | ||||
|  | ||||
|         } else if (name.endsWith("/*")) { | ||||
|           String prefix = name.substring(0, name.length() - 2); | ||||
|           if (!Repository.isValidRefName(prefix)) { | ||||
|             throw new InvalidNameException(); | ||||
|           } | ||||
|  | ||||
|         } else if (!Repository.isValidRefName(name)) { | ||||
|           throw new InvalidNameException(); | ||||
|           replace(config, toDelete, section); | ||||
|         } | ||||
|  | ||||
|         for (Permission permission : section.getPermissions()) { | ||||
|           for (PermissionRule rule : permission.getRules()) { | ||||
|             lookupGroup(rule); | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         config.replace(section); | ||||
|         toDelete.remove(section.getRefPattern()); | ||||
|       } | ||||
|  | ||||
|       for (String name : toDelete) { | ||||
|         if (projectControl.controlForRef(name).isOwner()) { | ||||
|         if (AccessSection.GLOBAL_CAPABILITIES.equals(name)) { | ||||
|           if (projectControl.isOwner()) { | ||||
|             config.remove(config.getAccessSection(name)); | ||||
|           } | ||||
|  | ||||
|         } else if (projectControl.controlForRef(name).isOwner()) { | ||||
|           config.remove(config.getAccessSection(name)); | ||||
|         } | ||||
|       } | ||||
| @@ -167,6 +174,17 @@ class ChangeProjectAccess extends Handler<ProjectAccess> { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private void replace(ProjectConfig config, Set<String> toDelete, | ||||
|       AccessSection section) throws NoSuchGroupException { | ||||
|     for (Permission permission : section.getPermissions()) { | ||||
|       for (PermissionRule rule : permission.getRules()) { | ||||
|         lookupGroup(rule); | ||||
|       } | ||||
|     } | ||||
|     config.replace(section); | ||||
|     toDelete.remove(section.getName()); | ||||
|   } | ||||
|  | ||||
|   private static List<AccessSection> mergeSections(List<AccessSection> src) { | ||||
|     Map<String, AccessSection> map = new LinkedHashMap<String, AccessSection>(); | ||||
|     for (AccessSection section : src) { | ||||
| @@ -174,11 +192,11 @@ class ChangeProjectAccess extends Handler<ProjectAccess> { | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       AccessSection prior = map.get(section.getRefPattern()); | ||||
|       AccessSection prior = map.get(section.getName()); | ||||
|       if (prior != null) { | ||||
|         prior.mergeFrom(section); | ||||
|       } else { | ||||
|         map.put(section.getRefPattern(), section); | ||||
|         map.put(section.getName(), section); | ||||
|       } | ||||
|     } | ||||
|     return new ArrayList<AccessSection>(map.values()); | ||||
| @@ -187,7 +205,7 @@ class ChangeProjectAccess extends Handler<ProjectAccess> { | ||||
|   private static Set<String> scanSectionNames(ProjectConfig config) { | ||||
|     Set<String> names = new HashSet<String>(); | ||||
|     for (AccessSection section : config.getAccessSections()) { | ||||
|       names.add(section.getRefPattern()); | ||||
|       names.add(section.getName()); | ||||
|     } | ||||
|     return names; | ||||
|   } | ||||
|   | ||||
| @@ -115,46 +115,55 @@ class ProjectAccessFactory extends Handler<ProjectAccess> { | ||||
|         new HashMap<AccountGroup.UUID, Boolean>(); | ||||
|  | ||||
|     for (AccessSection section : config.getAccessSections()) { | ||||
|       RefControl rc = pc.controlForRef(section.getRefPattern()); | ||||
|       if (rc.isOwner()) { | ||||
|         local.add(section); | ||||
|         ownerOf.add(section.getRefPattern()); | ||||
|       String name = section.getName(); | ||||
|       if (AccessSection.GLOBAL_CAPABILITIES.equals(name)) { | ||||
|         if (pc.isOwner()) { | ||||
|           local.add(section); | ||||
|           ownerOf.add(name); | ||||
|         } | ||||
|  | ||||
|       } else if (rc.isVisible()) { | ||||
|         // Filter the section to only add rules describing groups that | ||||
|         // are visible to the current-user. This includes any group the | ||||
|         // user is a member of, as well as groups they own or that | ||||
|         // are visible to all users. | ||||
|       } else if (AccessSection.isAccessSection(name)) { | ||||
|         RefControl rc = pc.controlForRef(name); | ||||
|         if (rc.isOwner()) { | ||||
|           local.add(section); | ||||
|           ownerOf.add(name); | ||||
|  | ||||
|         AccessSection dst = null; | ||||
|         for (Permission srcPerm : section.getPermissions()) { | ||||
|           Permission dstPerm = null; | ||||
|         } else if (rc.isVisible()) { | ||||
|           // Filter the section to only add rules describing groups that | ||||
|           // are visible to the current-user. This includes any group the | ||||
|           // user is a member of, as well as groups they own or that | ||||
|           // are visible to all users. | ||||
|  | ||||
|           for (PermissionRule srcRule : srcPerm.getRules()) { | ||||
|             AccountGroup.UUID group = srcRule.getGroup().getUUID(); | ||||
|             if (group == null) { | ||||
|               continue; | ||||
|             } | ||||
|           AccessSection dst = null; | ||||
|           for (Permission srcPerm : section.getPermissions()) { | ||||
|             Permission dstPerm = null; | ||||
|  | ||||
|             Boolean canSeeGroup = visibleGroups.get(group); | ||||
|             if (canSeeGroup == null) { | ||||
|               try { | ||||
|                 canSeeGroup = groupControlFactory.controlFor(group).isVisible(); | ||||
|               } catch (NoSuchGroupException e) { | ||||
|                 canSeeGroup = Boolean.FALSE; | ||||
|             for (PermissionRule srcRule : srcPerm.getRules()) { | ||||
|               AccountGroup.UUID group = srcRule.getGroup().getUUID(); | ||||
|               if (group == null) { | ||||
|                 continue; | ||||
|               } | ||||
|               visibleGroups.put(group, canSeeGroup); | ||||
|             } | ||||
|  | ||||
|             if (canSeeGroup) { | ||||
|               if (dstPerm == null) { | ||||
|                 if (dst == null) { | ||||
|                   dst = new AccessSection(section.getRefPattern()); | ||||
|                   local.add(dst); | ||||
|               Boolean canSeeGroup = visibleGroups.get(group); | ||||
|               if (canSeeGroup == null) { | ||||
|                 try { | ||||
|                   canSeeGroup = groupControlFactory.controlFor(group).isVisible(); | ||||
|                 } catch (NoSuchGroupException e) { | ||||
|                   canSeeGroup = Boolean.FALSE; | ||||
|                 } | ||||
|                 dstPerm = dst.getPermission(srcPerm.getName(), true); | ||||
|                 visibleGroups.put(group, canSeeGroup); | ||||
|               } | ||||
|  | ||||
|               if (canSeeGroup) { | ||||
|                 if (dstPerm == null) { | ||||
|                   if (dst == null) { | ||||
|                     dst = new AccessSection(name); | ||||
|                     local.add(dst); | ||||
|                   } | ||||
|                   dstPerm = dst.getPermission(srcPerm.getName(), true); | ||||
|                 } | ||||
|                 dstPerm.add(srcRule); | ||||
|               } | ||||
|               dstPerm.add(srcRule); | ||||
|             } | ||||
|           } | ||||
|         } | ||||
| @@ -170,17 +179,22 @@ class ProjectAccessFactory extends Handler<ProjectAccess> { | ||||
|  | ||||
|     final ProjectAccess detail = new ProjectAccess(); | ||||
|     detail.setRevision(config.getRevision().name()); | ||||
|     detail.setLocal(local); | ||||
|     detail.setOwnerOf(ownerOf); | ||||
|  | ||||
|     if (projectName.equals(wildProject)) { | ||||
|       if (pc.isOwner()) { | ||||
|         ownerOf.add(AccessSection.GLOBAL_CAPABILITIES); | ||||
|       } | ||||
|       detail.setInheritsFrom(null); | ||||
|  | ||||
|     } else if (config.getProject().getParent() != null) { | ||||
|       detail.setInheritsFrom(config.getProject().getParent()); | ||||
|  | ||||
|     } else { | ||||
|       detail.setInheritsFrom(wildProject); | ||||
|     } | ||||
|  | ||||
|     detail.setLocal(local); | ||||
|     detail.setOwnerOf(ownerOf); | ||||
|     return detail; | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -0,0 +1,135 @@ | ||||
| // 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.account; | ||||
|  | ||||
| import com.google.gerrit.common.data.AccessSection; | ||||
| import com.google.gerrit.common.data.GlobalCapability; | ||||
| 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.Project; | ||||
| import com.google.gerrit.server.CurrentUser; | ||||
| import com.google.gerrit.server.config.WildProjectName; | ||||
| import com.google.gerrit.server.project.NoSuchProjectException; | ||||
| import com.google.gerrit.server.project.ProjectCache; | ||||
| import com.google.gerrit.server.project.ProjectState; | ||||
| import com.google.inject.Inject; | ||||
| import com.google.inject.Provider; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.HashMap; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
|  | ||||
| /** Access control management for server-wide capabilities. */ | ||||
| public class CapabilityControl { | ||||
|   public static class Factory { | ||||
|     private final Project.NameKey wildProject; | ||||
|     private final ProjectCache projectCache; | ||||
|     private final Provider<CurrentUser> user; | ||||
|  | ||||
|     @Inject | ||||
|     Factory(@WildProjectName Project.NameKey wp, ProjectCache pc, | ||||
|         Provider<CurrentUser> cu) { | ||||
|       wildProject = wp; | ||||
|       projectCache = pc; | ||||
|       user = cu; | ||||
|     } | ||||
|  | ||||
|     public CapabilityControl controlFor() throws NoSuchProjectException { | ||||
|       final ProjectState p = projectCache.get(wildProject); | ||||
|       if (p == null) { | ||||
|         throw new NoSuchProjectException(wildProject); | ||||
|       } | ||||
|       return new CapabilityControl(p, user.get()); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private final ProjectState state; | ||||
|   private final CurrentUser user; | ||||
|   private Map<String, List<PermissionRule>> permissions; | ||||
|  | ||||
|   private CapabilityControl(ProjectState p, CurrentUser currentUser) { | ||||
|     state = p; | ||||
|     user = currentUser; | ||||
|   } | ||||
|  | ||||
|   /** Identity of the user the control will compute for. */ | ||||
|   public CurrentUser getCurrentUser() { | ||||
|     return user; | ||||
|   } | ||||
|  | ||||
|   /** True if the user has this permission. Works only for non labels. */ | ||||
|   public boolean canPerform(String permissionName) { | ||||
|     return !access(permissionName).isEmpty(); | ||||
|   } | ||||
|  | ||||
|   /** The range of permitted values associated with a label permission. */ | ||||
|   public PermissionRange getRange(String permission) { | ||||
|     if (GlobalCapability.hasRange(permission)) { | ||||
|       return toRange(permission, access(permission)); | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   private static PermissionRange toRange(String permissionName, | ||||
|       List<PermissionRule> ruleList) { | ||||
|     int min = 0; | ||||
|     int max = 0; | ||||
|     for (PermissionRule rule : ruleList) { | ||||
|       min = Math.min(min, rule.getMin()); | ||||
|       max = Math.max(max, rule.getMax()); | ||||
|     } | ||||
|     return new PermissionRange(permissionName, min, max); | ||||
|   } | ||||
|  | ||||
|   /** Rules for the given permission, or the empty list. */ | ||||
|   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. */ | ||||
|   private Map<String, List<PermissionRule>> permissions() { | ||||
|     if (permissions == null) { | ||||
|       permissions = new HashMap<String, List<PermissionRule>>(); | ||||
|       AccessSection section = | ||||
|           state.getConfig().getAccessSection(AccessSection.GLOBAL_CAPABILITIES); | ||||
|       for (Permission permission : section.getPermissions()) { | ||||
|         for (PermissionRule rule : permission.getRules()) { | ||||
|           if (matchGroup(rule.getGroup().getUUID())) { | ||||
|             if (!rule.getDeny()) { | ||||
|               List<PermissionRule> r = permissions.get(permission.getName()); | ||||
|               if (r == null) { | ||||
|                 r = new ArrayList<PermissionRule>(2); | ||||
|                 permissions.put(permission.getName(), r); | ||||
|               } | ||||
|               r.add(rule); | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return permissions; | ||||
|   } | ||||
|  | ||||
|   private boolean matchGroup(AccountGroup.UUID uuid) { | ||||
|     Set<AccountGroup.UUID> userGroups = getCurrentUser().getEffectiveGroups(); | ||||
|     return userGroups.contains(uuid); | ||||
|   } | ||||
| } | ||||
| @@ -20,6 +20,7 @@ import com.google.gerrit.reviewdb.ReviewDb; | ||||
| import com.google.gerrit.server.IdentifiedUser; | ||||
| import com.google.gerrit.server.RequestCleanup; | ||||
| import com.google.gerrit.server.account.AccountResolver; | ||||
| import com.google.gerrit.server.account.CapabilityControl; | ||||
| import com.google.gerrit.server.account.GroupControl; | ||||
| import com.google.gerrit.server.account.PerformCreateGroup; | ||||
| import com.google.gerrit.server.git.CreateCodeReviewNotes; | ||||
| @@ -56,6 +57,7 @@ public class GerritRequestModule extends FactoryModule { | ||||
|     bind(AccountResolver.class); | ||||
|     bind(ChangeQueryRewriter.class); | ||||
|  | ||||
|     bind(CapabilityControl.Factory.class).in(SINGLETON); | ||||
|     bind(ChangeControl.Factory.class).in(SINGLETON); | ||||
|     bind(GroupControl.Factory.class).in(SINGLETON); | ||||
|     bind(ProjectControl.Factory.class).in(SINGLETON); | ||||
|   | ||||
| @@ -18,6 +18,7 @@ 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.GlobalCapability; | ||||
| import com.google.gerrit.common.data.GroupReference; | ||||
| import com.google.gerrit.common.data.Permission; | ||||
| import com.google.gerrit.common.data.PermissionRule; | ||||
| @@ -54,6 +55,8 @@ public class ProjectConfig extends VersionedMetaData { | ||||
|   private static final String KEY_INHERIT_FROM = "inheritFrom"; | ||||
|   private static final String KEY_GROUP_PERMISSIONS = "exclusiveGroupPermissions"; | ||||
|  | ||||
|   private static final String CAPABILITY = "capability"; | ||||
|  | ||||
|   private static final String RECEIVE = "receive"; | ||||
|   private static final String KEY_REQUIRE_SIGNED_OFF_BY = "requireSignedOffBy"; | ||||
|   private static final String KEY_REQUIRE_CHANGE_ID = "requireChangeId"; | ||||
| @@ -115,7 +118,7 @@ public class ProjectConfig extends VersionedMetaData { | ||||
|  | ||||
|   public void remove(AccessSection section) { | ||||
|     if (section != null) { | ||||
|       accessSections.remove(section.getRefPattern()); | ||||
|       accessSections.remove(section.getName()); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @@ -126,7 +129,7 @@ public class ProjectConfig extends VersionedMetaData { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     accessSections.put(section.getRefPattern(), section); | ||||
|     accessSections.put(section.getName(), section); | ||||
|   } | ||||
|  | ||||
|   public GroupReference resolve(AccountGroup group) { | ||||
| @@ -234,38 +237,59 @@ public class ProjectConfig extends VersionedMetaData { | ||||
|         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) { | ||||
|                 error(new ValidationError(PROJECT_CONFIG, "Invalid rule in " + ACCESS | ||||
|                     + "." + refName + "." + varName + ": " | ||||
|                     + notRule.getMessage())); | ||||
|                 continue; | ||||
|               } | ||||
|  | ||||
|               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); | ||||
|                 error(new ValidationError(PROJECT_CONFIG, "group \"" | ||||
|                     + rule.getGroup().getName() + "\" not in " + GROUP_LIST)); | ||||
|               } | ||||
|  | ||||
|               rule.setGroup(ref); | ||||
|               perm.add(rule); | ||||
|             } | ||||
|             loadPermissionRules(rc, ACCESS, refName, varName, groupsByName, | ||||
|                 perm, perm.isLabel()); | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     AccessSection capability = null; | ||||
|     for (String varName : rc.getNames(CAPABILITY)) { | ||||
|       if (GlobalCapability.isCapability(varName)) { | ||||
|         if (capability == null) { | ||||
|           capability = new AccessSection(AccessSection.GLOBAL_CAPABILITIES); | ||||
|           accessSections.put(AccessSection.GLOBAL_CAPABILITIES, capability); | ||||
|         } | ||||
|         Permission perm = capability.getPermission(varName, true); | ||||
|         loadPermissionRules(rc, CAPABILITY, null, varName, groupsByName, perm, | ||||
|             GlobalCapability.hasRange(varName)); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private void loadPermissionRules(Config rc, String section, | ||||
|       String subsection, String varName, | ||||
|       Map<String, GroupReference> groupsByName, Permission perm, | ||||
|       boolean useRange) { | ||||
|     for (String ruleString : rc.getStringList(section, subsection, varName)) { | ||||
|       PermissionRule rule; | ||||
|       try { | ||||
|         rule = PermissionRule.fromString(ruleString, useRange); | ||||
|       } catch (IllegalArgumentException notRule) { | ||||
|         error(new ValidationError(PROJECT_CONFIG, "Invalid rule in " | ||||
|             + section | ||||
|             + (subsection != null ? "." + subsection : "") | ||||
|             + "." + varName + ": " | ||||
|             + notRule.getMessage())); | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       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); | ||||
|         error(new ValidationError(PROJECT_CONFIG, | ||||
|             "group \"" + ref.getName() + "\" not in " + GROUP_LIST)); | ||||
|       } | ||||
|  | ||||
|       rule.setGroup(ref); | ||||
|       perm.add(rule); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private Map<String, GroupReference> readGroupList() throws IOException { | ||||
| @@ -321,8 +345,38 @@ public class ProjectConfig extends VersionedMetaData { | ||||
|     set(rc, SUBMIT, null, KEY_MERGE_CONTENT, p.isUseContentMerge()); | ||||
|  | ||||
|     Set<AccountGroup.UUID> keepGroups = new HashSet<AccountGroup.UUID>(); | ||||
|     AccessSection capability = accessSections.get(AccessSection.GLOBAL_CAPABILITIES); | ||||
|     if (capability != null) { | ||||
|       Set<String> have = new HashSet<String>(); | ||||
|       for (Permission permission : sort(capability.getPermissions())) { | ||||
|         have.add(permission.getName().toLowerCase()); | ||||
|  | ||||
|         boolean needRange = GlobalCapability.hasRange(permission.getName()); | ||||
|         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(CAPABILITY, null, permission.getName(), rules); | ||||
|       } | ||||
|       for (String varName : rc.getNames(CAPABILITY)) { | ||||
|         if (GlobalCapability.isCapability(varName) | ||||
|             && !have.contains(varName.toLowerCase())) { | ||||
|           rc.unset(CAPABILITY, null, varName); | ||||
|         } | ||||
|       } | ||||
|     } else { | ||||
|       rc.unsetSection(CAPABILITY, null); | ||||
|     } | ||||
|  | ||||
|     for (AccessSection as : sort(accessSections.values())) { | ||||
|       String refName = as.getRefPattern(); | ||||
|       String refName = as.getName(); | ||||
|       if (AccessSection.GLOBAL_CAPABILITIES.equals(refName)) { | ||||
|         continue; | ||||
|       } | ||||
|  | ||||
|       StringBuilder doNotInherit = new StringBuilder(); | ||||
|       for (Permission perm : sort(as.getPermissions())) { | ||||
|   | ||||
| @@ -234,7 +234,7 @@ public class ProjectControl { | ||||
|         // that would render this to be false. | ||||
|         // | ||||
|         if (groups.contains(rule.getGroup().getUUID()) | ||||
|             && controlForRef(section.getRefPattern()).canPerform(permissionName)) { | ||||
|             && controlForRef(section.getName()).canPerform(permissionName)) { | ||||
|           return true; | ||||
|         } | ||||
|       } | ||||
| @@ -267,7 +267,7 @@ public class ProjectControl { | ||||
|     for (AccessSection section : access()) { | ||||
|       Permission permission = section.getPermission(permissionName); | ||||
|       if (permission != null) { | ||||
|         all.add(section.getRefPattern()); | ||||
|         all.add(section.getName()); | ||||
|       } | ||||
|     } | ||||
|     return all; | ||||
|   | ||||
| @@ -389,7 +389,7 @@ public class RefControl { | ||||
|   } | ||||
|  | ||||
|   private boolean appliesToRef(AccessSection section) { | ||||
|     String refPattern = section.getRefPattern(); | ||||
|     String refPattern = section.getName(); | ||||
|  | ||||
|     if (isTemplate(refPattern)) { | ||||
|       ParamertizedString template = new ParamertizedString(refPattern); | ||||
| @@ -476,7 +476,7 @@ public class RefControl { | ||||
|     final AccountGroup.UUID group; | ||||
|  | ||||
|     SeenRule(AccessSection section, Permission permission, PermissionRule rule) { | ||||
|       refPattern = section.getRefPattern(); | ||||
|       refPattern = section.getName(); | ||||
|       permissionName = permission.getName(); | ||||
|       group = rule.getGroup().getUUID(); | ||||
|     } | ||||
| @@ -540,7 +540,7 @@ public class RefControl { | ||||
|     } | ||||
|  | ||||
|     public int compare(AccessSection a, AccessSection b) { | ||||
|       return compare(a.getRefPattern(), b.getRefPattern()); | ||||
|       return compare(a.getName(), b.getName()); | ||||
|     } | ||||
|  | ||||
|     private int compare(final String pattern1, final String pattern2) { | ||||
|   | ||||
| @@ -14,13 +14,17 @@ | ||||
|  | ||||
| package com.google.gerrit.server.query.change; | ||||
|  | ||||
| import com.google.gerrit.common.data.GlobalCapability; | ||||
| import com.google.gerrit.common.data.PermissionRange; | ||||
| import com.google.gerrit.reviewdb.Change; | ||||
| import com.google.gerrit.reviewdb.PatchSet; | ||||
| import com.google.gerrit.reviewdb.ReviewDb; | ||||
| import com.google.gerrit.server.CurrentUser; | ||||
| import com.google.gerrit.server.account.CapabilityControl; | ||||
| import com.google.gerrit.server.events.ChangeAttribute; | ||||
| import com.google.gerrit.server.events.EventFactory; | ||||
| import com.google.gerrit.server.events.QueryStats; | ||||
| import com.google.gerrit.server.project.NoSuchProjectException; | ||||
| import com.google.gerrit.server.query.Predicate; | ||||
| import com.google.gerrit.server.query.QueryParseException; | ||||
| import com.google.gson.Gson; | ||||
| @@ -65,7 +69,7 @@ public class QueryProcessor { | ||||
|   private final ChangeQueryRewriter queryRewriter; | ||||
|   private final Provider<ReviewDb> db; | ||||
|  | ||||
|   private int defaultLimit = 500; | ||||
|   private int defaultLimit; | ||||
|   private OutputFormat outputFormat = OutputFormat.TEXT; | ||||
|   private boolean includePatchSets; | ||||
|   private boolean includeCurrentPatchSet; | ||||
| @@ -77,11 +81,19 @@ public class QueryProcessor { | ||||
|   @Inject | ||||
|   QueryProcessor(EventFactory eventFactory, | ||||
|       ChangeQueryBuilder.Factory queryBuilder, CurrentUser currentUser, | ||||
|       ChangeQueryRewriter queryRewriter, Provider<ReviewDb> db) { | ||||
|       ChangeQueryRewriter queryRewriter, Provider<ReviewDb> db, | ||||
|       CapabilityControl.Factory ctl) throws NoSuchProjectException { | ||||
|     this.eventFactory = eventFactory; | ||||
|     this.queryBuilder = queryBuilder.create(currentUser); | ||||
|     this.queryRewriter = queryRewriter; | ||||
|     this.db = db; | ||||
|  | ||||
|     PermissionRange range = ctl.controlFor().getRange(GlobalCapability.QUERY_LIMIT); | ||||
|     if (range != null) { | ||||
|       defaultLimit = range.getMax(); | ||||
|     } else { | ||||
|       defaultLimit = GlobalCapability.getRange(GlobalCapability.QUERY_LIMIT).getDefaultMax(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public void setIncludePatchSets(boolean on) { | ||||
| @@ -106,6 +118,13 @@ public class QueryProcessor { | ||||
|         new BufferedWriter( // | ||||
|             new OutputStreamWriter(outputStream, "UTF-8"))); | ||||
|     try { | ||||
|       if (defaultLimit == 0) { | ||||
|         ErrorMessage m = new ErrorMessage(); | ||||
|         m.message = "query disabled"; | ||||
|         show(m); | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       try { | ||||
|         final QueryStats stats = new QueryStats(); | ||||
|         stats.runTimeMilliseconds = System.currentTimeMillis(); | ||||
|   | ||||
| @@ -16,7 +16,9 @@ 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.GlobalCapability; | ||||
| 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.AccountGroupName; | ||||
| @@ -235,6 +237,11 @@ public class SchemaCreator { | ||||
|       p.setDescription("Rights inherited by all other projects"); | ||||
|       p.setUseContributorAgreements(false); | ||||
|  | ||||
|       AccessSection capabilities = config.getAccessSection(AccessSection.GLOBAL_CAPABILITIES, true); | ||||
|       PermissionRange.WithDefaults queryLimit = GlobalCapability.getRange(GlobalCapability.QUERY_LIMIT); | ||||
|       capabilities.getPermission(GlobalCapability.QUERY_LIMIT, true) | ||||
|         .add(rule(config, anonymous, queryLimit.getDefaultMax(), queryLimit.getDefaultMax())); | ||||
|  | ||||
|       AccessSection all = config.getAccessSection(AccessSection.ALL, true); | ||||
|       AccessSection heads = config.getAccessSection(AccessSection.HEADS, true); | ||||
|       AccessSection meta = config.getAccessSection(GitRepositoryManager.REF_CONFIG, true); | ||||
| @@ -270,6 +277,13 @@ public class SchemaCreator { | ||||
|     return new PermissionRule(config.resolve(group)); | ||||
|   } | ||||
|  | ||||
|   private PermissionRule rule(ProjectConfig config, AccountGroup group, | ||||
|       int min, int max) { | ||||
|     PermissionRule rule = new PermissionRule(config.resolve(group)); | ||||
|     rule.setRange(min, max); | ||||
|     return rule; | ||||
|   } | ||||
|  | ||||
|   private void initVerifiedCategory(final ReviewDb c) throws OrmException { | ||||
|     final ApprovalCategory cat; | ||||
|     final ArrayList<ApprovalCategoryValue> vals; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Shawn O. Pearce
					Shawn O. Pearce