Merge branch 'stable' * stable: Fix SLF4J StaticLoggerBinder warning ExportReviewNotes: Dump submitted changes to refs/notes/review Conflicts: gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java Change-Id: I147ba04af6d8af4177dcdd94d55e8b1c7d9b10e9
diff --git a/Documentation/cmd-create-project.txt b/Documentation/cmd-create-project.txt index 71c0d1f..e00e45b 100644 --- a/Documentation/cmd-create-project.txt +++ b/Documentation/cmd-create-project.txt
@@ -13,7 +13,6 @@ [--branch <REF>] \ [\--owner <GROUP> ...] \ [\--parent <NAME>] \ - [\--permissions-only] \ [\--description <DESC>] \ [\--submit-type <TYPE>] \ [\--use-content-merge] \ @@ -71,11 +70,6 @@ through. If not specified, the parent is set to the default project `\-- All Projects \--`. -\--permissions-only:: - Create the project only to serve as a parent for other - projects. The new project's Git repository will not be - initialized, and cannot be cloned. - \--description:: Initial description of the project. If not specified, no description is stored.
diff --git a/Documentation/pgm-init.txt b/Documentation/pgm-init.txt index 9644e92..d3094fd 100644 --- a/Documentation/pgm-init.txt +++ b/Documentation/pgm-init.txt
@@ -11,7 +11,6 @@ 'java' -jar gerrit.war 'init' -d <SITE_PATH> [\--batch] - [\--import-projects] [\--no-auto-start] DESCRIPTION @@ -21,7 +20,7 @@ into a newly created `$site_path`. If run an an existing `$site_path`, init will upgrade some resources -as necessary. This can be useful to import newly created projects. +as necessary. OPTIONS ------- @@ -30,12 +29,6 @@ configuration defaults are chosen based on the whims of the Gerrit developers. -\--import-projects:: - Recursively search - link:config-gerrit.html#gerrit.basePath[gerrit.basePath] - for any Git repositories not yet registered as a project, - and initializes a new project for them. - \--no-auto-start:: Don't automatically start the daemon after initializing a newly created site path. This permits the administartor
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java index efe311f..db4f348 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/PageLinks.java
@@ -54,6 +54,10 @@ return "change," + ps.getParentKey().toString() + ",patchset=" + ps.get(); } + public static String toProjectAcceess(final Project.NameKey p) { + return "admin,project," + p.get() + ",access"; + } + public static String toAccountDashboard(final AccountInfo acct) { return toAccountDashboard(acct.getId()); }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/AccessSection.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccessSection.java new file mode 100644 index 0000000..44a00d5 --- /dev/null +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/AccessSection.java
@@ -0,0 +1,140 @@ +// Copyright (C) 2010 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.common.data; + +import com.google.gerrit.reviewdb.Project; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** Portion of a {@link Project} describing access rules. */ +public class AccessSection implements Comparable<AccessSection> { + /** Pattern that matches all references in a project. */ + public static final String ALL = "refs/*"; + + /** Pattern that matches all branches in a project. */ + public static final String HEADS = "refs/heads/*"; + + /** Prefix that triggers a regular expression pattern. */ + public static final String REGEX_PREFIX = "^"; + + /** @return true if the name is likely to be a valid access section name. */ + public static boolean isAccessSection(String name) { + return name.startsWith("refs/") || name.startsWith("^refs/"); + } + + protected String refPattern; + protected List<Permission> permissions; + + protected AccessSection() { + } + + public AccessSection(String refPattern) { + setRefPattern(refPattern); + } + + public String getRefPattern() { + return refPattern; + } + + public void setRefPattern(String refPattern) { + this.refPattern = refPattern; + } + + public List<Permission> getPermissions() { + if (permissions == null) { + permissions = new ArrayList<Permission>(); + } + return permissions; + } + + public void setPermissions(List<Permission> list) { + Set<String> names = new HashSet<String>(); + for (Permission p : list) { + if (!names.add(p.getName().toLowerCase())) { + throw new IllegalArgumentException(); + } + } + + permissions = list; + } + + public Permission getPermission(String name) { + return getPermission(name, false); + } + + public Permission getPermission(String name, boolean create) { + for (Permission p : getPermissions()) { + if (p.getName().equalsIgnoreCase(name)) { + return p; + } + } + + if (create) { + Permission p = new Permission(name); + permissions.add(p); + return p; + } else { + return null; + } + } + + public void remove(Permission permission) { + if (permission != null) { + removePermission(permission.getName()); + } + } + + public void removePermission(String name) { + if (permissions != null) { + for (Iterator<Permission> itr = permissions.iterator(); itr.hasNext();) { + if (name.equalsIgnoreCase(itr.next().getName())) { + itr.remove(); + } + } + } + } + + public void mergeFrom(AccessSection section) { + for (Permission src : section.getPermissions()) { + Permission dst = getPermission(src.getName()); + if (dst != null) { + dst.mergeFrom(src); + } else { + permissions.add(dst); + } + } + } + + @Override + public int compareTo(AccessSection o) { + return comparePattern().compareTo(o.comparePattern()); + } + + private String comparePattern() { + if (getRefPattern().startsWith(REGEX_PREFIX)) { + return getRefPattern().substring(REGEX_PREFIX.length()); + } + return getRefPattern(); + } + + @Override + public String toString() { + return "AccessSection[" + getRefPattern() + "]"; + } +}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalType.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalType.java index ea9aedb..7d03457 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalType.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalType.java
@@ -31,6 +31,7 @@ protected short maxNegative; protected short maxPositive; + private transient List<Integer> intList; private transient Map<Short, ApprovalCategoryValue> byValue; protected ApprovalType() { @@ -56,6 +57,9 @@ maxPositive = values.get(values.size() - 1).getValue(); } } + + // Force the label name to pre-compute so we don't have data race conditions. + getCategory().getLabelName(); } public ApprovalCategory getCategory() { @@ -107,4 +111,16 @@ } } } + + public List<Integer> getValuesAsList() { + if (intList == null) { + intList = new ArrayList<Integer>(values.size()); + for (ApprovalCategoryValue acv : values) { + intList.add(Integer.valueOf(acv.getValue())); + } + Collections.sort(intList); + Collections.reverse(intList); + } + return intList; + } }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalTypes.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalTypes.java index 1b6d4a3..0518010 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalTypes.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ApprovalTypes.java
@@ -19,20 +19,17 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; public class ApprovalTypes { protected List<ApprovalType> approvalTypes; - protected List<ApprovalType> actionTypes; - private transient Map<ApprovalCategory.Id, ApprovalType> byCategoryId; + private transient Map<ApprovalCategory.Id, ApprovalType> byId; + private transient Map<String, ApprovalType> byLabel; protected ApprovalTypes() { } - public ApprovalTypes(final List<ApprovalType> approvals, - final List<ApprovalType> actions) { + public ApprovalTypes(final List<ApprovalType> approvals) { approvalTypes = approvals; - actionTypes = actions; byCategory(); } @@ -40,33 +37,35 @@ return approvalTypes; } - public List<ApprovalType> getActionTypes() { - return actionTypes; - } - - public ApprovalType getApprovalType(final ApprovalCategory.Id id) { + public ApprovalType byId(final ApprovalCategory.Id id) { return byCategory().get(id); } - public Set<ApprovalCategory.Id> getApprovalCategories() { - return byCategory().keySet(); - } - private Map<ApprovalCategory.Id, ApprovalType> byCategory() { - if (byCategoryId == null) { - byCategoryId = new HashMap<ApprovalCategory.Id, ApprovalType>(); - if (actionTypes != null) { - for (final ApprovalType t : actionTypes) { - byCategoryId.put(t.getCategory().getId(), t); - } - } - + if (byId == null) { + byId = new HashMap<ApprovalCategory.Id, ApprovalType>(); if (approvalTypes != null) { for (final ApprovalType t : approvalTypes) { - byCategoryId.put(t.getCategory().getId(), t); + byId.put(t.getCategory().getId(), t); } } } - return byCategoryId; + return byId; + } + + public ApprovalType byLabel(String labelName) { + return byLabel().get(labelName.toLowerCase()); + } + + private Map<String, ApprovalType> byLabel() { + if (byLabel == null) { + byLabel = new HashMap<String, ApprovalType>(); + if (approvalTypes != null) { + for (ApprovalType t : approvalTypes) { + byLabel.put(t.getCategory().getLabelName().toLowerCase(), t); + } + } + } + return byLabel; } }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java index 572fd7a..f196c05 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ChangeDetail.java
@@ -39,10 +39,10 @@ protected List<PatchSet> patchSets; protected List<ApprovalDetail> approvals; protected Set<ApprovalCategory.Id> missingApprovals; + protected boolean canSubmit; protected List<ChangeMessage> messages; protected PatchSet.Id currentPatchSetId; protected PatchSetDetail currentDetail; - protected Set<ApprovalCategory.Id> currentActions; public ChangeDetail() { } @@ -87,6 +87,14 @@ canRevert = a; } + public boolean canSubmit() { + return canSubmit; + } + + public void setCanSubmit(boolean a) { + canSubmit = a; + } + public Change getChange() { return change; } @@ -153,14 +161,6 @@ missingApprovals = a; } - public Set<ApprovalCategory.Id> getCurrentActions() { - return currentActions; - } - - public void setCurrentActions(Set<ApprovalCategory.Id> a) { - currentActions = a; - } - public boolean isCurrentPatchSet(final PatchSetDetail detail) { return currentPatchSetId != null && detail.getPatchSet().getId().equals(currentPatchSetId);
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java index ce508cc..6d4b889 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupAdminService.java
@@ -21,8 +21,8 @@ import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwtjsonrpc.client.RemoteJsonService; import com.google.gwtjsonrpc.client.RpcImpl; -import com.google.gwtjsonrpc.client.VoidResult; import com.google.gwtjsonrpc.client.RpcImpl.Version; +import com.google.gwtjsonrpc.client.VoidResult; import java.util.List; import java.util.Set; @@ -36,7 +36,8 @@ void createGroup(String newName, AsyncCallback<AccountGroup.Id> callback); @SignInRequired - void groupDetail(AccountGroup.Id groupId, AsyncCallback<GroupDetail> callback); + void groupDetail(AccountGroup.Id groupId, AccountGroup.UUID uuid, + AsyncCallback<GroupDetail> callback); @SignInRequired void changeGroupDescription(AccountGroup.Id groupId, String description,
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java new file mode 100644 index 0000000..cd6e172 --- /dev/null +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/GroupReference.java
@@ -0,0 +1,76 @@ +// Copyright (C) 2010 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.common.data; + +import com.google.gerrit.reviewdb.AccountGroup; + +/** Describes a group within a projects {@link AccessSection}s. */ +public class GroupReference implements Comparable<GroupReference> { + /** @return a new reference to the given group description. */ + public static GroupReference forGroup(AccountGroup group) { + return new GroupReference(group.getGroupUUID(), group.getName()); + } + + protected String uuid; + protected String name; + + protected GroupReference() { + } + + public GroupReference(AccountGroup.UUID uuid, String name) { + setUUID(uuid); + setName(name); + } + + public AccountGroup.UUID getUUID() { + return uuid != null ? new AccountGroup.UUID(uuid) : null; + } + + public void setUUID(AccountGroup.UUID newUUID) { + uuid = newUUID != null ? newUUID.get() : null; + } + + public String getName() { + return name; + } + + public void setName(String newName) { + this.name = newName; + } + + @Override + public int compareTo(GroupReference o) { + return uuid(this).compareTo(uuid(o)); + } + + private static String uuid(GroupReference a) { + return a.getUUID() != null ? a.getUUID().get() : "?"; + } + + @Override + public int hashCode() { + return uuid(this).hashCode(); + } + + @Override + public boolean equals(Object o) { + return o instanceof GroupReference && compareTo((GroupReference) o) == 0; + } + + @Override + public String toString() { + return "Group[" + getName() + " / " + getUUID() + "]"; + } +}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/InheritedRefRight.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/InheritedRefRight.java deleted file mode 100644 index 4dc998b..0000000 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/InheritedRefRight.java +++ /dev/null
@@ -1,74 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.common.data; - -import com.google.gerrit.reviewdb.RefRight; - -/** - * Additional data about a {@link RefRight} not normally loaded: defines if a - * right is inherited from a parent structure (e.g. a parent project). - */ -public class InheritedRefRight { - private RefRight right; - private boolean inherited; - private boolean owner; - - /** - * Creates a instance of a {@link RefRight} with data about inheritance - */ - protected InheritedRefRight() { - } - - /** - * Creates a instance of a {@link RefRight} with data about inheritance - * - * @param right the right - * @param inherited true if the right is inherited, false otherwise - * @param owner true if right is owned by current user, false otherwise - */ - public InheritedRefRight(RefRight right, boolean inherited, boolean owner) { - this.right = right; - this.inherited = inherited; - this.owner = owner; - } - - public RefRight getRight() { - return right; - } - - public boolean isInherited() { - return inherited; - } - - public boolean isOwner() { - return owner; - } - - @Override - public boolean equals(Object o) { - if (o instanceof InheritedRefRight) { - InheritedRefRight a = this; - InheritedRefRight b = (InheritedRefRight) o; - return a.getRight().equals(b.getRight()) - && a.isInherited() == b.isInherited(); - } - return false; - } - - @Override - public int hashCode() { - return getRight().hashCode(); - } -}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetPublishDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetPublishDetail.java index 7a0ada3..273f18d 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetPublishDetail.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PatchSetPublishDetail.java
@@ -15,39 +15,35 @@ package com.google.gerrit.common.data; import com.google.gerrit.reviewdb.ApprovalCategory; -import com.google.gerrit.reviewdb.ApprovalCategoryValue; import com.google.gerrit.reviewdb.Change; import com.google.gerrit.reviewdb.PatchLineComment; import com.google.gerrit.reviewdb.PatchSetApproval; import com.google.gerrit.reviewdb.PatchSetInfo; import java.util.List; -import java.util.Map; -import java.util.Set; public class PatchSetPublishDetail { protected AccountInfoCache accounts; protected PatchSetInfo patchSetInfo; protected Change change; protected List<PatchLineComment> drafts; - protected Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> allowed; - protected Map<ApprovalCategory.Id, PatchSetApproval> given; - protected boolean isSubmitAllowed; + protected List<PermissionRange> labels; + protected List<PatchSetApproval> given; + protected boolean canSubmit; - public Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> getAllowed() { - return allowed; + public List<PermissionRange> getLabels() { + return labels; } - public void setAllowed( - Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> allowed) { - this.allowed = allowed; + public void setLabels(List<PermissionRange> labels) { + this.labels = labels; } - public Map<ApprovalCategory.Id, PatchSetApproval> getGiven() { + public List<PatchSetApproval> getGiven() { return given; } - public void setGiven(Map<ApprovalCategory.Id, PatchSetApproval> given) { + public void setGiven(List<PatchSetApproval> given) { this.given = given; } @@ -67,8 +63,8 @@ this.drafts = drafts; } - public void setSubmitAllowed(boolean allowed) { - isSubmitAllowed = allowed; + public void setCanSubmit(boolean allowed) { + canSubmit = allowed; } public AccountInfoCache getAccounts() { @@ -87,20 +83,25 @@ return drafts; } - public boolean isAllowed(final ApprovalCategory.Id id) { - final Set<ApprovalCategoryValue.Id> s = getAllowed(id); - return s != null && !s.isEmpty(); + public PermissionRange getRange(final String permissionName) { + for (PermissionRange s : labels) { + if (s.getName().equals(permissionName)) { + return s; + } + } + return null; } - public Set<ApprovalCategoryValue.Id> getAllowed(final ApprovalCategory.Id id) { - return allowed.get(id); + public PatchSetApproval getChangeApproval(ApprovalCategory.Id id) { + for (PatchSetApproval a : given) { + if (a.getCategoryId().equals(id)) { + return a; + } + } + return null; } - public PatchSetApproval getChangeApproval(final ApprovalCategory.Id id) { - return given.get(id); - } - - public boolean isSubmitAllowed() { - return isSubmitAllowed; + public boolean canSubmit() { + return canSubmit; } }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java new file mode 100644 index 0000000..c27d9d9 --- /dev/null +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/Permission.java
@@ -0,0 +1,201 @@ +// Copyright (C) 2010 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.common.data; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** A single permission within an {@link AccessSection} of a project. */ +public class Permission implements Comparable<Permission> { + public static final String CREATE = "create"; + public static final String FORGE_AUTHOR = "forgeAuthor"; + public static final String FORGE_COMMITTER = "forgeCommitter"; + public static final String FORGE_SERVER = "forgeServerAsCommitter"; + public static final String LABEL = "label-"; + public static final String OWNER = "owner"; + public static final String PUSH = "push"; + public static final String PUSH_MERGE = "pushMerge"; + public static final String PUSH_TAG = "pushTag"; + public static final String READ = "read"; + public static final String SUBMIT = "submit"; + + private static final List<String> NAMES_LC; + + static { + NAMES_LC = new ArrayList<String>(); + NAMES_LC.add(OWNER.toLowerCase()); + NAMES_LC.add(READ.toLowerCase()); + NAMES_LC.add(CREATE.toLowerCase()); + NAMES_LC.add(FORGE_AUTHOR.toLowerCase()); + NAMES_LC.add(FORGE_COMMITTER.toLowerCase()); + NAMES_LC.add(FORGE_SERVER.toLowerCase()); + NAMES_LC.add(PUSH.toLowerCase()); + NAMES_LC.add(PUSH_MERGE.toLowerCase()); + NAMES_LC.add(PUSH_TAG.toLowerCase()); + NAMES_LC.add(LABEL.toLowerCase()); + NAMES_LC.add(SUBMIT.toLowerCase()); + } + + /** @return true if the name is recognized as a permission name. */ + public static boolean isPermission(String varName) { + String lc = varName.toLowerCase(); + if (lc.startsWith(LABEL)) { + return LABEL.length() < lc.length(); + } + return NAMES_LC.contains(lc); + } + + /** @return true if the permission name is actually for a review label. */ + public static boolean isLabel(String varName) { + return varName.startsWith(LABEL) && LABEL.length() < varName.length(); + } + + /** @return permission name for the given review label. */ + public static String forLabel(String labelName) { + return LABEL + labelName; + } + + protected String name; + protected boolean exclusiveGroup; + protected List<PermissionRule> rules; + + protected Permission() { + } + + public Permission(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public boolean isLabel() { + return isLabel(getName()); + } + + public String getLabel() { + if (isLabel()) { + return getName().substring(LABEL.length()); + } + return null; + } + + public Boolean getExclusiveGroup() { + // Only permit exclusive group behavior on non OWNER permissions, + // otherwise an owner might lose access to a delegated subspace. + // + return exclusiveGroup && !OWNER.equals(getName()); + } + + public void setExclusiveGroup(Boolean newExclusiveGroup) { + exclusiveGroup = newExclusiveGroup; + } + + public List<PermissionRule> getRules() { + initRules(); + return rules; + } + + public void setRules(List<PermissionRule> list) { + rules = list; + } + + public void add(PermissionRule rule) { + initRules(); + rules.add(rule); + } + + public void remove(PermissionRule rule) { + if (rule != null) { + removeRule(rule.getGroup()); + } + } + + public void removeRule(GroupReference group) { + if (rules != null) { + for (Iterator<PermissionRule> itr = rules.iterator(); itr.hasNext();) { + if (sameGroup(itr.next(), group)) { + itr.remove(); + } + } + } + } + + public PermissionRule getRule(GroupReference group) { + return getRule(group, false); + } + + public PermissionRule getRule(GroupReference group, boolean create) { + initRules(); + + for (PermissionRule r : rules) { + if (sameGroup(r, group)) { + return r; + } + } + + if (create) { + PermissionRule r = new PermissionRule(group); + rules.add(r); + return r; + } else { + return null; + } + } + + void mergeFrom(Permission src) { + for (PermissionRule srcRule : src.getRules()) { + PermissionRule dstRule = getRule(srcRule.getGroup()); + if (dstRule != null) { + dstRule.mergeFrom(srcRule); + } else { + add(srcRule); + } + } + } + + private static boolean sameGroup(PermissionRule rule, GroupReference group) { + if (group.getUUID() != null) { + return group.getUUID().equals(rule.getGroup().getUUID()); + + } else if (group.getName() != null) { + return group.getName().equals(rule.getGroup().getName()); + + } else { + return false; + } + } + + private void initRules() { + if (rules == null) { + rules = new ArrayList<PermissionRule>(4); + } + } + + @Override + public int compareTo(Permission b) { + int cmp = index(this) - index(b); + if (cmp == 0) getName().compareTo(b.getName()); + return cmp; + } + + private static int index(Permission a) { + String lc = a.isLabel() ? Permission.LABEL : a.getName().toLowerCase(); + int index = NAMES_LC.indexOf(lc); + return 0 <= index ? index : NAMES_LC.size(); + } +}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRange.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRange.java new file mode 100644 index 0000000..bc2dadd --- /dev/null +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRange.java
@@ -0,0 +1,95 @@ +// Copyright (C) 2010 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.common.data; + +public class PermissionRange implements Comparable<PermissionRange> { + protected String name; + protected int min; + protected int max; + + protected PermissionRange() { + } + + public PermissionRange(String name, int min, int max) { + this.name = name; + + if (min <= max) { + this.min = min; + this.max = max; + } else { + this.min = max; + this.max = min; + } + } + + public String getName() { + return name; + } + + public boolean isLabel() { + return Permission.isLabel(getName()); + } + + public String getLabel() { + return isLabel() ? getName().substring(Permission.LABEL.length()) : null; + } + + public int getMin() { + return min; + } + + public int getMax() { + return max; + } + + /** True if the value is within the range. */ + public boolean contains(int value) { + return getMin() <= value && value <= getMax(); + } + + /** Normalize the value to fit within the bounds of the range. */ + public int squash(int value) { + return Math.min(Math.max(getMin(), value), getMax()); + } + + /** True both {@link #getMin()} and {@link #getMax()} are 0. */ + public boolean isEmpty() { + return getMin() == 0 && getMax() == 0; + } + + @Override + public int compareTo(PermissionRange o) { + return getName().compareTo(o.getName()); + } + + @Override + public String toString() { + StringBuilder r = new StringBuilder(); + if (getMin() < 0 && getMax() == 0) { + r.append(getMin()); + r.append(' '); + } else { + if (getMin() != getMax()) { + if (0 <= getMin()) r.append('+'); + r.append(getMin()); + r.append(".."); + } + if (0 <= getMax()) r.append('+'); + r.append(getMax()); + r.append(' '); + } + return r.toString(); + } +}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java new file mode 100644 index 0000000..5c73c52 --- /dev/null +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/PermissionRule.java
@@ -0,0 +1,203 @@ +// Copyright (C) 2010 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.common.data; + +public class PermissionRule implements Comparable<PermissionRule> { + public static enum Action { + ALLOW, DENY; + } + + protected boolean deny; + protected boolean force; + protected int min; + protected int max; + protected GroupReference group; + + public PermissionRule() { + } + + public PermissionRule(GroupReference group) { + this.group = group; + } + + public Action getAction() { + return deny ? Action.DENY : Action.ALLOW; + } + + public void setAction(Action action) { + if (action == null) { + throw new NullPointerException("action"); + } + setDeny(action == Action.DENY); + } + + public boolean getDeny() { + return deny; + } + + public void setDeny(boolean newDeny) { + deny = newDeny; + } + + public Boolean getForce() { + return force; + } + + public void setForce(Boolean newForce) { + force = newForce; + } + + public Integer getMin() { + return min; + } + + public void setMin(Integer min) { + this.min = min; + } + + public void setMax(Integer max) { + this.max = max; + } + + public Integer getMax() { + return max; + } + + public void setRange(int newMin, int newMax) { + if (newMax < newMin) { + min = newMax; + max = newMin; + } else { + min = newMin; + max = newMax; + } + } + + public GroupReference getGroup() { + return group; + } + + public void setGroup(GroupReference newGroup) { + group = newGroup; + } + + void mergeFrom(PermissionRule src) { + setDeny(getDeny() || src.getDeny()); + setForce(getForce() || src.getForce()); + setRange(Math.min(getMin(), src.getMin()), Math.max(getMax(), src.getMax())); + } + + @Override + public int compareTo(PermissionRule o) { + int cmp = deny(this) - deny(o); + if (cmp == 0) cmp = range(o) - range(this); + if (cmp == 0) cmp = group(this).compareTo(group(o)); + return cmp; + } + + private static int deny(PermissionRule a) { + return a.getDeny() ? 1 : 0; + } + + private static int range(PermissionRule a) { + return Math.abs(a.getMin()) + Math.abs(a.getMax()); + } + + private static String group(PermissionRule a) { + return a.getGroup().getName() != null ? a.getGroup().getName() : ""; + } + + @Override + public String toString() { + return asString(true); + } + + public String asString(boolean canUseRange) { + StringBuilder r = new StringBuilder(); + + if (getDeny()) { + r.append("deny "); + } + + if (getForce()) { + r.append("+force "); + } + + if (canUseRange && (getMin() != 0 || getMax() != 0)) { + if (0 <= getMin()) r.append('+'); + r.append(getMin()); + r.append(".."); + if (0 <= getMax()) r.append('+'); + r.append(getMax()); + r.append(' '); + } + + r.append("group "); + r.append(getGroup().getName()); + + return r.toString(); + } + + public static PermissionRule fromString(String src, boolean mightUseRange) { + final String orig = src; + final PermissionRule rule = new PermissionRule(); + + src = src.trim(); + + if (src.startsWith("deny ")) { + rule.setDeny(true); + src = src.substring(5).trim(); + } + + if (src.startsWith("+force ")) { + rule.setForce(true); + src = src.substring("+force ".length()).trim(); + } + + if (mightUseRange && !src.startsWith("group ")) { + int sp = src.indexOf(' '); + String range = src.substring(0, sp); + + if (range.matches("^([+-]\\d+)\\.\\.([+-]\\d)$")) { + int dotdot = range.indexOf(".."); + int min = parseInt(range.substring(0, dotdot)); + int max = parseInt(range.substring(dotdot + 2)); + rule.setRange(min, max); + } else { + throw new IllegalArgumentException("Invalid range in rule: " + orig); + } + + src = src.substring(sp + 1).trim(); + } + + if (src.startsWith("group ")) { + src = src.substring(6).trim(); + GroupReference group = new GroupReference(); + group.setName(src); + rule.setGroup(group); + } else { + throw new IllegalArgumentException("Rule must include group: " + orig); + } + + return rule; + } + + private static int parseInt(String value) { + if (value.startsWith("+")) { + value = value.substring(1); + } + return Integer.parseInt(value); + } +}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAccess.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAccess.java new file mode 100644 index 0000000..2fbf512 --- /dev/null +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAccess.java
@@ -0,0 +1,66 @@ +// 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 com.google.gerrit.reviewdb.Project; + +import java.util.List; +import java.util.Set; + +public class ProjectAccess { + protected String revision; + protected Project.NameKey inheritsFrom; + protected List<AccessSection> local; + protected Set<String> ownerOf; + + public ProjectAccess() { + } + + public String getRevision() { + return revision; + } + + public void setRevision(String name) { + revision = name; + } + + public Project.NameKey getInheritsFrom() { + return inheritsFrom; + } + + public void setInheritsFrom(Project.NameKey name) { + inheritsFrom = name; + } + + public List<AccessSection> getLocal() { + return local; + } + + public void setLocal(List<AccessSection> as) { + local = as; + } + + public boolean isOwnerOf(AccessSection section) { + return getOwnerOf().contains(section.getRefPattern()); + } + + public Set<String> getOwnerOf() { + return ownerOf; + } + + public void setOwnerOf(Set<String> refs) { + ownerOf = refs; + } +}
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java index b5a986f..cd02785 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectAdminService.java
@@ -15,10 +15,8 @@ package com.google.gerrit.common.data; import com.google.gerrit.common.auth.SignInRequired; -import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.Branch; import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.RefRight; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwtjsonrpc.client.RemoteJsonService; import com.google.gwtjsonrpc.client.RpcImpl; @@ -34,18 +32,17 @@ void projectDetail(Project.NameKey projectName, AsyncCallback<ProjectDetail> callback); + void projectAccess(Project.NameKey projectName, + AsyncCallback<ProjectAccess> callback); + @SignInRequired void changeProjectSettings(Project update, AsyncCallback<ProjectDetail> callback); @SignInRequired - void deleteRight(Project.NameKey projectName, Set<RefRight.Key> ids, - AsyncCallback<ProjectDetail> callback); - - @SignInRequired - void addRight(Project.NameKey projectName, ApprovalCategory.Id categoryId, - String groupName, String refName, short min, short max, - AsyncCallback<ProjectDetail> callback); + void changeProjectAccess(Project.NameKey projectName, String baseRevision, + String message, List<AccessSection> sections, + AsyncCallback<ProjectAccess> callback); void listBranches(Project.NameKey projectName, AsyncCallback<ListBranchesResult> callback);
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectDetail.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectDetail.java index 2aa8c62..02aaf80 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectDetail.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/ProjectDetail.java
@@ -14,16 +14,10 @@ package com.google.gerrit.common.data; -import com.google.gerrit.reviewdb.AccountGroup; import com.google.gerrit.reviewdb.Project; -import java.util.List; -import java.util.Map; - public class ProjectDetail { public Project project; - public Map<AccountGroup.Id, AccountGroup> groups; - public List<InheritedRefRight> rights; public boolean canModifyDescription; public boolean canModifyMergeType; public boolean canModifyAgreements; @@ -36,14 +30,6 @@ project = p; } - public void setGroups(final Map<AccountGroup.Id, AccountGroup> g) { - groups = g; - } - - public void setRights(final List<InheritedRefRight> r) { - rights = r; - } - public void setCanModifyDescription(final boolean cmd) { canModifyDescription = cmd; }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java b/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java index 9dae169..976004f 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/data/SuggestService.java
@@ -14,7 +14,6 @@ package com.google.gerrit.common.data; -import com.google.gerrit.reviewdb.AccountGroupName; import com.google.gerrit.reviewdb.Project; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwtjsonrpc.client.RemoteJsonService; @@ -32,5 +31,5 @@ AsyncCallback<List<AccountInfo>> callback); void suggestAccountGroup(String query, int limit, - AsyncCallback<List<AccountGroupName>> callback); + AsyncCallback<List<GroupReference>> callback); }
diff --git a/gerrit-common/src/main/java/com/google/gerrit/common/errors/NoSuchGroupException.java b/gerrit-common/src/main/java/com/google/gerrit/common/errors/NoSuchGroupException.java index 4e117b1..179bc3a 100644 --- a/gerrit-common/src/main/java/com/google/gerrit/common/errors/NoSuchGroupException.java +++ b/gerrit-common/src/main/java/com/google/gerrit/common/errors/NoSuchGroupException.java
@@ -26,10 +26,18 @@ this(key, null); } + public NoSuchGroupException(final AccountGroup.UUID key) { + this(key, null); + } + public NoSuchGroupException(final AccountGroup.Id key, final Throwable why) { super(MESSAGE + key.toString(), why); } + public NoSuchGroupException(final AccountGroup.UUID key, final Throwable why) { + super(MESSAGE + key.toString(), why); + } + public NoSuchGroupException(final AccountGroup.NameKey k) { this(k, null); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml index ec0f74f..d3d990c 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/GerritGwtUI.gwt.xml
@@ -14,6 +14,7 @@ limitations under the License. --> <module rename-to="gerrit"> + <inherits name='com.google.gwt.editor.Editor'/> <inherits name='com.google.gwt.user.User'/> <inherits name='com.google.gwt.resources.Resources'/> <inherits name='com.google.gwt.user.theme.chrome.Chrome'/>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java index 2b3ace4..8b82a30 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/Dispatcher.java
@@ -87,6 +87,10 @@ return "admin,group," + id.toString(); } + public static String toGroup(final AccountGroup.UUID uuid) { + return "admin,group,uuid-" + uuid.toString(); + } + public static String toProjectAdmin(final Project.NameKey n, final String tab) { return "admin,project," + n.toString() + "," + tab; } @@ -411,6 +415,10 @@ private Screen select() { String p; + p = "admin,group,uuid-"; + if (token.startsWith(p)) + return new AccountGroupScreen(AccountGroup.UUID.parse(skip(p, token))); + p = "admin,group,"; if (token.startsWith(p)) return new AccountGroupScreen(AccountGroup.Id.parse(skip(p, token)));
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessRightEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessRightEditor.java deleted file mode 100644 index 8f6831b..0000000 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessRightEditor.java +++ /dev/null
@@ -1,404 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.client.admin; - -import com.google.gerrit.client.Gerrit; -import com.google.gerrit.client.rpc.GerritCallback; -import com.google.gerrit.client.ui.AccountGroupSuggestOracle; -import com.google.gerrit.client.ui.HintTextBox; -import com.google.gerrit.client.ui.RPCSuggestOracle; -import com.google.gerrit.common.data.ApprovalType; -import com.google.gerrit.common.data.ProjectDetail; -import com.google.gerrit.reviewdb.AccountGroup; -import com.google.gerrit.reviewdb.ApprovalCategory; -import com.google.gerrit.reviewdb.ApprovalCategoryValue; -import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.RefRight; -import com.google.gwt.event.dom.client.ChangeEvent; -import com.google.gwt.event.dom.client.ChangeHandler; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.event.dom.client.ClickHandler; -import com.google.gwt.event.dom.client.KeyCodes; -import com.google.gwt.event.dom.client.KeyPressEvent; -import com.google.gwt.event.dom.client.KeyPressHandler; -import com.google.gwt.event.logical.shared.HasValueChangeHandlers; -import com.google.gwt.event.logical.shared.ValueChangeEvent; -import com.google.gwt.event.logical.shared.ValueChangeHandler; -import com.google.gwt.event.shared.HandlerRegistration; -import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.Grid; -import com.google.gwt.user.client.ui.ListBox; -import com.google.gwt.user.client.ui.SuggestBox; - -public class AccessRightEditor extends Composite - implements HasValueChangeHandlers<ProjectDetail> { - private Project.NameKey projectKey; - private ListBox catBox; - private HintTextBox nameTxt; - private SuggestBox nameSug; - private HintTextBox referenceTxt; - private ListBox topBox; - private ListBox botBox; - private Button addBut; - private Button clearBut; - - public AccessRightEditor(final Project.NameKey key) { - projectKey = key; - - initWidgets(); - initCategories(); - - final Grid grid = new Grid(5, 2); - grid.setText(0, 0, Util.C.columnApprovalCategory() + ":"); - grid.setWidget(0, 1, catBox); - - grid.setText(1, 0, Util.C.columnGroupName() + ":"); - grid.setWidget(1, 1, nameSug); - - grid.setText(2, 0, Util.C.columnRefName() + ":"); - grid.setWidget(2, 1, referenceTxt); - - grid.setText(3, 0, Util.C.columnRightRange() + ":"); - grid.setWidget(3, 1, topBox); - - grid.setText(4, 0, ""); - grid.setWidget(4, 1, botBox); - - FlowPanel fp = new FlowPanel(); - fp.setStyleName(Gerrit.RESOURCES.css().addSshKeyPanel()); - - fp.add(grid); - fp.add(addBut); - fp.add(clearBut); - initWidget(fp); - } - - protected void initWidgets() { - catBox = new ListBox(); - catBox.addChangeHandler(new ChangeHandler() { - @Override - public void onChange(final ChangeEvent event) { - updateCategorySelection(); - } - }); - - nameTxt = new HintTextBox(); - nameSug = new SuggestBox(new RPCSuggestOracle( - new AccountGroupSuggestOracle()), nameTxt); - nameTxt.setVisibleLength(50); - nameTxt.setHintText(Util.C.defaultAccountGroupName()); - - referenceTxt = new HintTextBox(); - referenceTxt.setVisibleLength(50); - referenceTxt.setText(""); - referenceTxt.addKeyPressHandler(new KeyPressHandler() { - @Override - public void onKeyPress(KeyPressEvent event) { - if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) { - doAddNewRight(); - } - } - }); - - topBox = new ListBox(); - botBox = new ListBox(); - - addBut = new Button(Util.C.buttonAddProjectRight()); - addBut.addClickHandler(new ClickHandler() { - @Override - public void onClick(final ClickEvent event) { - doAddNewRight(); - } - }); - - clearBut = new Button(Util.C.buttonClearProjectRight()); - clearBut.addClickHandler(new ClickHandler() { - @Override - public void onClick(final ClickEvent event) { - clear(); - } - }); - } - - protected void initCategories() { - for (final ApprovalType at : Gerrit.getConfig().getApprovalTypes() - .getApprovalTypes()) { - final ApprovalCategory c = at.getCategory(); - catBox.addItem(c.getName(), c.getId().get()); - } - for (final ApprovalType at : Gerrit.getConfig().getApprovalTypes() - .getActionTypes()) { - final ApprovalCategory c = at.getCategory(); - if (Gerrit.getConfig().getWildProject().equals(projectKey) - && !c.getId().canBeOnWildProject()) { - // Giving out control of the WILD_PROJECT to other groups beyond - // Administrators is dangerous. Having control over WILD_PROJECT - // is about the same as having Administrator access as users are - // able to affect grants in all projects on the system. - // - continue; - } - catBox.addItem(c.getName(), c.getId().get()); - } - - if (catBox.getItemCount() > 0) { - catBox.setSelectedIndex(0); - updateCategorySelection(); - } - } - - public void enableForm(final boolean on) { - final boolean canAdd = on && catBox.getItemCount() > 0; - addBut.setEnabled(canAdd); - clearBut.setEnabled(canAdd); - nameTxt.setEnabled(canAdd); - referenceTxt.setEnabled(canAdd); - catBox.setEnabled(canAdd); - topBox.setEnabled(canAdd); - botBox.setEnabled(canAdd); - } - - public void clear() { - setCat(null); - setName(""); - setReference(""); - } - - public void load(final RefRight right, final AccountGroup group) { - final ApprovalType atype = - Gerrit.getConfig().getApprovalTypes().getApprovalType( - right.getApprovalCategoryId()); - - setCat(atype != null ? atype.getCategory().getName() - : right.getApprovalCategoryId().get() ); - - setName(group.getName()); - setReference(right.getRefPatternForDisplay()); - - setRange(atype.getCategory().isRange() ? atype.getValue(right.getMinValue()) - : null, atype.getValue(right.getMaxValue()) ); - } - - protected void doAddNewRight() { - final ApprovalType at = getApprovalType(); - ApprovalCategoryValue min = getMin(at); - ApprovalCategoryValue max = getMax(at); - - if (at == null || min == null || max == null) { - return; - } - - final String groupName = nameSug.getText(); - if ("".equals(groupName) - || Util.C.defaultAccountGroupName().equals(groupName)) { - return; - } - - final String refPattern = referenceTxt.getText(); - - addBut.setEnabled(false); - Util.PROJECT_SVC.addRight(projectKey, at.getCategory().getId(), - groupName, refPattern, min.getValue(), max.getValue(), - new GerritCallback<ProjectDetail>() { - public void onSuccess(final ProjectDetail result) { - addBut.setEnabled(true); - nameSug.setText(""); - referenceTxt.setText(""); - ValueChangeEvent.fire(AccessRightEditor.this, result); - } - - @Override - public void onFailure(final Throwable caught) { - addBut.setEnabled(true); - super.onFailure(caught); - } - }); - } - - protected void updateCategorySelection() { - final ApprovalType at = getApprovalType(); - - if (at == null || at.getValues().isEmpty()) { - topBox.setEnabled(false); - botBox.setEnabled(false); - referenceTxt.setEnabled(false); - addBut.setEnabled(false); - clearBut.setEnabled(false); - return; - } - - updateRanges(at); - } - - protected void updateRanges(final ApprovalType at) { - ApprovalCategoryValue min = null, max = null, last = null; - - topBox.clear(); - botBox.clear(); - - for(final ApprovalCategoryValue v : at.getValues()) { - final int nval = v.getValue(); - final String vStr = String.valueOf(nval); - - String nStr = vStr + ": " + v.getName(); - if (nval > 0) { - nStr = "+" + nStr; - } - - topBox.addItem(nStr, vStr); - botBox.addItem(nStr, vStr); - - if (min == null || nval < 0) { - min = v; - } else if (max == null && nval > 0) { - max = v; - } - last = v; - } - - if (max == null) { - max = last; - } - - if (ApprovalCategory.READ.equals(at.getCategory().getId())) { - // Special case; for READ the most logical range is just - // +1 READ, so assume that as the default for both. - min = max; - } - - if (! at.getCategory().isRange()) { - max = null; - } - - setRange(min, max); - } - - protected void setCat(final String cat) { - if (cat == null) { - catBox.setSelectedIndex(0); - } else { - setSelectedText(catBox, cat); - } - updateCategorySelection(); - } - - protected void setName(final String name) { - nameTxt.setText(name); - } - - protected void setReference(final String ref) { - referenceTxt.setText(ref); - } - - protected void setRange(final ApprovalCategoryValue min, - final ApprovalCategoryValue max) { - if (min == null || max == null) { - botBox.setVisible(false); - if (max != null) { - setSelectedValue(topBox, "" + max.getValue()); - return; - } - } else { - botBox.setVisible(true); - setSelectedValue(botBox, "" + max.getValue()); - } - setSelectedValue(topBox, "" + min.getValue()); - } - - private ApprovalType getApprovalType() { - int idx = catBox.getSelectedIndex(); - if (idx < 0) { - return null; - } - return Gerrit.getConfig().getApprovalTypes().getApprovalType( - new ApprovalCategory.Id(catBox.getValue(idx))); - } - - public ApprovalCategoryValue getMin(ApprovalType at) { - final ApprovalCategoryValue top = getTop(at); - final ApprovalCategoryValue bot = getBot(at); - if (bot == null) { - for (final ApprovalCategoryValue v : at.getValues()) { - if (0 <= v.getValue() && v.getValue() <= top.getValue()) { - return v; - } - } - return at.getMin(); - } - - if (top.getValue() > bot.getValue()) { - return bot; - } - return top; - } - - public ApprovalCategoryValue getMax(ApprovalType at) { - final ApprovalCategoryValue top = getTop(at); - final ApprovalCategoryValue bot = getBot(at); - if (bot == null || bot.getValue() < top.getValue()) { - return top; - } - return bot; - } - - protected ApprovalCategoryValue getTop(ApprovalType at) { - int idx = topBox.getSelectedIndex(); - if (idx < 0) { - return null; - } - return at.getValue(Short.parseShort(topBox.getValue(idx))); - } - - protected ApprovalCategoryValue getBot(ApprovalType at) { - int idx = botBox.getSelectedIndex(); - if (idx < 0 || ! botBox.isVisible()) { - return null; - } - return at.getValue(Short.parseShort(botBox.getValue(idx))); - } - - public HandlerRegistration addValueChangeHandler( - final ValueChangeHandler<ProjectDetail> handler) { - return addHandler(handler, ValueChangeEvent.getType()); - } - - public static boolean setSelectedText(ListBox box, String text) { - if (text == null) { - return false; - } - for (int i =0 ; i < box.getItemCount(); i++) { - if (text.equals(box.getItemText(i))) { - box.setSelectedIndex(i); - return true; - } - } - return false; - } - - public static boolean setSelectedValue(ListBox box, String value) { - if (value == null) { - return false; - } - for (int i =0 ; i < box.getItemCount(); i++) { - if (value.equals(box.getValue(i))) { - box.setSelectedIndex(i); - return true; - } - } - return false; - } -}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java new file mode 100644 index 0000000..fdcaa00 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.java
@@ -0,0 +1,238 @@ +// 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.gerrit.client.Gerrit; +import com.google.gerrit.common.data.AccessSection; +import com.google.gerrit.common.data.ApprovalType; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.common.data.ProjectAccess; +import com.google.gwt.core.client.GWT; +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.core.client.Scheduler.ScheduledCommand; +import com.google.gwt.dom.client.DivElement; +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.ValueAwareEditor; +import com.google.gwt.editor.client.adapters.EditorSource; +import com.google.gwt.editor.client.adapters.ListEditor; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.MouseOutEvent; +import com.google.gwt.event.dom.client.MouseOverEvent; +import com.google.gwt.event.logical.shared.ValueChangeEvent; +import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.uibinder.client.UiHandler; +import com.google.gwt.user.client.ui.Anchor; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HTMLPanel; +import com.google.gwt.user.client.ui.ValueListBox; + +import java.util.ArrayList; +import java.util.List; + +public class AccessSectionEditor extends Composite implements + Editor<AccessSection>, ValueAwareEditor<AccessSection> { + interface Binder extends UiBinder<HTMLPanel, AccessSectionEditor> { + } + + private static final Binder uiBinder = GWT.create(Binder.class); + + @UiField + ValueEditor<String> refPattern; + + @UiField + FlowPanel permissionContainer; + ListEditor<Permission, PermissionEditor> permissions; + + @UiField + DivElement addContainer; + @UiField(provided = true) + @Editor.Ignore + ValueListBox<String> permissionSelector; + + @UiField + SpanElement deletedName; + + @UiField + Anchor deleteSection; + + @UiField + DivElement normal; + @UiField + DivElement deleted; + + private final ProjectAccess projectAccess; + private AccessSection value; + private boolean editing; + private boolean readOnly; + private boolean isDeleted; + + public AccessSectionEditor(ProjectAccess access) { + projectAccess = access; + + permissionSelector = + new ValueListBox<String>(PermissionNameRenderer.INSTANCE); + permissionSelector.addValueChangeHandler(new ValueChangeHandler<String>() { + @Override + public void onValueChange(ValueChangeEvent<String> event) { + if (!Util.C.addPermission().equals(event.getValue())) { + onAddPermission(event.getValue()); + } + } + }); + + initWidget(uiBinder.createAndBindUi(this)); + permissions = ListEditor.of(new PermissionEditorSource()); + } + + @UiHandler("deleteSection") + void onDeleteHover(MouseOverEvent event) { + normal.addClassName(AdminResources.I.css().deleteSectionHover()); + } + + @UiHandler("deleteSection") + void onDeleteNonHover(MouseOutEvent event) { + normal.removeClassName(AdminResources.I.css().deleteSectionHover()); + } + + @UiHandler("deleteSection") + void onDeleteSection(ClickEvent event) { + isDeleted = true; + deletedName.setInnerText(refPattern.getValue()); + normal.getStyle().setDisplay(Display.NONE); + deleted.getStyle().setDisplay(Display.BLOCK); + } + + @UiHandler("undoDelete") + void onUndoDelete(ClickEvent event) { + isDeleted = false; + deleted.getStyle().setDisplay(Display.NONE); + normal.getStyle().setDisplay(Display.BLOCK); + } + + void onAddPermission(String varName) { + Permission p = value.getPermission(varName, true); + permissions.getList().add(p); + rebuildPermissionSelector(); + } + + void editRefPattern() { + refPattern.edit(); + Scheduler.get().scheduleDeferred(new ScheduledCommand() { + @Override + public void execute() { + refPattern.setFocus(true); + }}); + } + + void enableEditing() { + readOnly = false; + addContainer.getStyle().setDisplay(Display.BLOCK); + rebuildPermissionSelector(); + } + + boolean isDeleted() { + return isDeleted; + } + + @Override + public void setValue(AccessSection value) { + this.value = value; + this.readOnly = !editing || !projectAccess.isOwnerOf(value); + + refPattern.setEnabled(!readOnly); + deleteSection.setVisible(!readOnly); + + if (readOnly) { + addContainer.getStyle().setDisplay(Display.NONE); + } else { + enableEditing(); + } + } + + void setEditing(final boolean editing) { + this.editing = editing; + } + + 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); + } + } + for (String varName : Util.C.permissionNames().keySet()) { + if (value.getPermission(varName) == null) { + perms.add(varName); + } + } + if (perms.isEmpty()) { + addContainer.getStyle().setDisplay(Display.NONE); + } else { + addContainer.getStyle().setDisplay(Display.BLOCK); + perms.add(0, Util.C.addPermission()); + permissionSelector.setValue(Util.C.addPermission()); + permissionSelector.setAcceptableValues(perms); + } + } + + @Override + public void flush() { + List<Permission> src = permissions.getList(); + List<Permission> keep = new ArrayList<Permission>(src.size()); + + for (int i = 0; i < src.size(); i++) { + PermissionEditor e = (PermissionEditor) permissionContainer.getWidget(i); + if (!e.isDeleted()) { + keep.add(src.get(i)); + } + } + value.setPermissions(keep); + } + + @Override + public void onPropertyChange(String... paths) { + } + + @Override + public void setDelegate(EditorDelegate<AccessSection> delegate) { + } + + private class PermissionEditorSource extends EditorSource<PermissionEditor> { + @Override + public PermissionEditor create(int index) { + PermissionEditor subEditor = new PermissionEditor(readOnly, value); + permissionContainer.insert(subEditor, index); + return subEditor; + } + + @Override + public void dispose(PermissionEditor subEditor) { + subEditor.removeFromParent(); + } + + @Override + public void setIndex(PermissionEditor subEditor, int index) { + permissionContainer.insert(subEditor, index); + } + } +}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.ui.xml new file mode 100644 index 0000000..fdae4ed --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccessSectionEditor.ui.xml
@@ -0,0 +1,157 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +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. +--> +<ui:UiBinder + xmlns:ui='urn:ui:com.google.gwt.uibinder' + xmlns:g='urn:import:com.google.gwt.user.client.ui' + xmlns:e='urn:import:com.google.gwt.editor.ui.client' + xmlns:my='urn:import:com.google.gerrit.client.admin' + ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat' + ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator' + ui:generateLocales='default,en' + > +<ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/> +<ui:style> + @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor; + @eval trimColor com.google.gerrit.client.Gerrit.getTheme().trimColor; + + .panel { + position: relative; + } + + .content { + margin-top: 4px; + margin-bottom: 4px; + padding-bottom: 2px; + } + + .normal { + background-color: trimColor; + } + + .deleted { + padding-left: 7px; + padding-bottom: 2px; + } + + .header { + padding-left: 5px; + padding-right: 5px; + } + .headerText { + vertical-align: top; + white-space: nowrap; + font-weight: bold; + } + .headerTable { + border: 0; + width: 100%; + padding-right: 40px; + } + + .header:hover { + background-color: selectionColor; + } + + .refName { + width: 100%; + } + .refNameEdit { + width: 100%; + } + + .permissionList { + margin-left: 5px; + margin-right: 5px; + } + + .addContainer { + padding-left: 16px; + padding-right: 16px; + font-size: 80%; + } + .addContainer:hover { + background-color: selectionColor; + } + .addSelector { + font-size: 80%; + } + + .deleteIcon { + position: absolute; + top: 5px; + right: 17px; + } + + .undoIcon { + position: absolute; + top: 2px; + right: 17px; + } +</ui:style> + +<g:HTMLPanel styleName='{style.panel}'> +<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 width='100%'> + <my:ValueEditor + ui:field='refPattern' + addStyleNames='{style.refName}' + editTitle='Edit reference pattern'> + <ui:attribute name='editTitle'/> + <my:editor> + <my:RefPatternBox styleName='{style.refNameEdit}'/> + </my:editor> + </my:ValueEditor> + </td> + </tr></table> + + <g:Anchor + ui:field='deleteSection' + href='javascript:void' + styleName='{style.deleteIcon} {res.css.deleteIcon}' + title='Delete this section (and nested rules)'> + <ui:attribute name='title'/> + </g:Anchor> + </div> + + <g:FlowPanel + ui:field='permissionContainer' + styleName='{style.permissionList}'/> + <div ui:field='addContainer' class='{style.addContainer}'> + <g:ValueListBox + ui:field='permissionSelector' + styleName='{style.addSelector}' /> + </div> +</div> + +<div + ui:field='deleted' + class='{style.deleted} {res.css.deleted}' + style='display: none'> + <ui:msg>Reference <span ui:field='deletedName'/> was deleted</ui:msg> + <g:Anchor + ui:field='undoDelete' + href='javascript:void' + styleName='{style.undoIcon} {res.css.undoIcon}' + title='Undo deletion'> + <ui:attribute name='title'/> + </g:Anchor> +</div> +</g:HTMLPanel> +</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java index 593ab34..9f8117a 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AccountGroupScreen.java
@@ -63,7 +63,9 @@ import java.util.List; public class AccountGroupScreen extends AccountScreen { - private final AccountGroup.Id groupId; + private AccountGroup.Id groupId; + private AccountGroup.UUID groupUUID; + private AccountInfoCache accounts = AccountInfoCache.empty(); private GroupInfoCache groups = GroupInfoCache.empty(); private MemberTable members; @@ -106,24 +108,31 @@ groupId = toShow; } + public AccountGroupScreen(final AccountGroup.UUID toShow) { + groupUUID = toShow; + } + @Override protected void onLoad() { super.onLoad(); - Util.GROUP_SVC.groupDetail(groupId, new ScreenLoadCallback<GroupDetail>( - this) { - @Override - protected void preDisplay(final GroupDetail result) { - enableForm(result.canModify); - saveName.setVisible(result.canModify); - saveOwner.setVisible(result.canModify); - saveDesc.setVisible(result.canModify); - saveGroupOptions.setVisible(result.canModify); - delMember.setVisible(result.canModify); - saveType.setVisible(result.canModify); - delInclude.setVisible(result.canModify); - display(result); - } - }); + Util.GROUP_SVC.groupDetail(groupId, groupUUID, + new ScreenLoadCallback<GroupDetail>(this) { + @Override + protected void preDisplay(final GroupDetail result) { + groupId = result.group.getId(); + groupUUID = result.group.getGroupUUID(); + display(result); + + enableForm(result.canModify); + saveName.setVisible(result.canModify); + saveOwner.setVisible(result.canModify); + saveDesc.setVisible(result.canModify); + saveGroupOptions.setVisible(result.canModify); + delMember.setVisible(result.canModify); + saveType.setVisible(result.canModify); + delInclude.setVisible(result.canModify); + } + }); } @Override
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java index 4631a29..4f95a70 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.java
@@ -16,6 +16,8 @@ import com.google.gwt.i18n.client.Constants; +import java.util.Map; + public interface AdminConstants extends Constants { String defaultAccountName(); String defaultAccountGroupName(); @@ -101,4 +103,14 @@ String noGroupSelected(); String errorNoMatchingGroups(); String errorNoGitRepository(); + + String addPermission(); + Map<String,String> permissionNames(); + + String refErrorEmpty(); + String refErrorBeginSlash(); + String refErrorDoubleSlash(); + String refErrorNoSpace(); + String refErrorPrintable(); + String errorsMustBeFixed(); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties index 4bad368..e9575cf 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminConstants.properties
@@ -83,3 +83,36 @@ noGroupSelected = (No group selected) errorNoMatchingGroups = No Matching Groups errorNoGitRepository = No Git Repository + + +addPermission = Add Permission ... + +# Permission Names +permissionNames = \ + create, \ + forgeAuthor, \ + forgeCommitter, \ + forgeServerAsCommitter, \ + owner, \ + push, \ + pushMerge, \ + pushTag, \ + read, \ + submit +create = Create Reference +forgeAuthor = Forge Author Identity +forgeCommitter = Forge Committer Identity +forgeServerAsCommitter = Forge Server Identity +owner = Owner +push = Push +pushMerge = Push Merge Commit +pushTag = Push Annotated Tag +read = Read +submit = Submit + +refErrorEmpty = Reference must be supplied +refErrorBeginSlash = Reference must not start with '/' +refErrorDoubleSlash = References cannot contain '//' +refErrorNoSpace = References cannot contain spaces +refErrorPrintable = References may contain only printable characters +errorsMustBeFixed = Errors must be fixed before committing changes.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_49.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminCss.java similarity index 69% rename from gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_49.java rename to gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminCss.java index 0977ee9..55a9bf3 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_49.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminCss.java
@@ -12,15 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -package com.google.gerrit.server.schema; +package com.google.gerrit.client.admin; -import com.google.inject.Inject; -import com.google.inject.Provider; +import com.google.gwt.resources.client.CssResource; -public class Schema_49 extends SchemaVersion { +public interface AdminCss extends CssResource { + String deleteIcon(); + String undoIcon(); - @Inject - Schema_49(Provider<Schema_48> prior) { - super(prior); - } + String deleted(); + String deletedBorder(); + + String deleteSectionHover(); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java index ab3541e..9ce3ccf 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.java
@@ -18,6 +18,7 @@ public interface AdminMessages extends Messages { String group(String name); + String label(String name); String project(String name); String deletedGroup(int id); }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties index 6feb69a..60d9c70 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminMessages.properties
@@ -1,3 +1,4 @@ group = Group {0} +label = Label {0} project = Project {0} deletedGroup = Deleted Group {0}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminResources.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminResources.java new file mode 100644 index 0000000..cd366f3 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/AdminResources.java
@@ -0,0 +1,38 @@ +// 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.core.client.GWT; +import com.google.gwt.resources.client.ClientBundle; +import com.google.gwt.resources.client.ImageResource; + +public interface AdminResources extends ClientBundle { + public static final AdminResources I = GWT.create(AdminResources.class); + + @Source("admin.css") + AdminCss css(); + + @Source("editText.png") + public ImageResource editText(); + + @Source("deleteNormal.png") + public ImageResource deleteNormal(); + + @Source("deleteHover.png") + public ImageResource deleteHover(); + + @Source("undoNormal.png") + public ImageResource undoNormal(); +}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupReferenceBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupReferenceBox.java new file mode 100644 index 0000000..b51f0c0 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupReferenceBox.java
@@ -0,0 +1,143 @@ +// 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.gerrit.client.ui.AccountGroupSuggestOracle; +import com.google.gerrit.client.ui.RPCSuggestOracle; +import com.google.gerrit.common.data.GroupReference; +import com.google.gwt.editor.client.LeafValueEditor; +import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.event.dom.client.KeyPressEvent; +import com.google.gwt.event.dom.client.KeyPressHandler; +import com.google.gwt.event.dom.client.KeyUpEvent; +import com.google.gwt.event.dom.client.KeyUpHandler; +import com.google.gwt.event.logical.shared.CloseEvent; +import com.google.gwt.event.logical.shared.CloseHandler; +import com.google.gwt.event.logical.shared.HasCloseHandlers; +import com.google.gwt.event.logical.shared.HasSelectionHandlers; +import com.google.gwt.event.logical.shared.SelectionEvent; +import com.google.gwt.event.logical.shared.SelectionHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.Focusable; +import com.google.gwt.user.client.ui.SuggestBox; +import com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay; +import com.google.gwt.user.client.ui.SuggestOracle.Suggestion; +import com.google.gwtexpui.globalkey.client.NpTextBox; + +public class GroupReferenceBox extends Composite implements + LeafValueEditor<GroupReference>, HasSelectionHandlers<GroupReference>, + HasCloseHandlers<GroupReferenceBox>, Focusable { + private final DefaultSuggestionDisplay suggestions; + private final NpTextBox textBox; + private final AccountGroupSuggestOracle oracle; + private final SuggestBox suggestBox; + + private boolean submitOnSelection; + + public GroupReferenceBox() { + suggestions = new DefaultSuggestionDisplay(); + textBox = new NpTextBox(); + oracle = new AccountGroupSuggestOracle(); + suggestBox = new SuggestBox( // + new RPCSuggestOracle(oracle), // + textBox, // + suggestions); + initWidget(suggestBox); + + suggestBox.addKeyPressHandler(new KeyPressHandler() { + @Override + public void onKeyPress(KeyPressEvent event) { + submitOnSelection = false; + + if (event.getCharCode() == KeyCodes.KEY_ENTER) { + if (suggestions.isSuggestionListShowing()) { + submitOnSelection = true; + } else { + SelectionEvent.fire(GroupReferenceBox.this, getValue()); + } + } + } + }); + suggestBox.addKeyUpHandler(new KeyUpHandler() { + @Override + public void onKeyUp(KeyUpEvent event) { + if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) { + suggestBox.setText(""); + CloseEvent.fire(GroupReferenceBox.this, GroupReferenceBox.this); + } + } + }); + suggestBox.addSelectionHandler(new SelectionHandler<Suggestion>() { + @Override + public void onSelection(SelectionEvent<Suggestion> event) { + if (submitOnSelection) { + submitOnSelection = false; + SelectionEvent.fire(GroupReferenceBox.this, getValue()); + } + } + }); + } + + public void setVisibleLength(int len) { + textBox.setVisibleLength(len); + } + + @Override + public HandlerRegistration addSelectionHandler( + SelectionHandler<GroupReference> handler) { + return addHandler(handler, SelectionEvent.getType()); + } + + @Override + public HandlerRegistration addCloseHandler( + CloseHandler<GroupReferenceBox> handler) { + return addHandler(handler, CloseEvent.getType()); + } + + @Override + public GroupReference getValue() { + String name = suggestBox.getText(); + if (name != null && !name.isEmpty()) { + return new GroupReference(oracle.getUUID(name), name); + } else { + return null; + } + } + + @Override + public void setValue(GroupReference value) { + suggestBox.setText(value != null ? value.getName() : ""); + } + + @Override + public int getTabIndex() { + return suggestBox.getTabIndex(); + } + + @Override + public void setTabIndex(int index) { + suggestBox.setTabIndex(index); + } + + public void setFocus(boolean focused) { + suggestBox.setFocus(focused); + } + + @Override + public void setAccessKey(char key) { + suggestBox.setAccessKey(key); + } +}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java new file mode 100644 index 0000000..c21844c --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.java
@@ -0,0 +1,297 @@ +// 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.gerrit.client.Gerrit; +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.GroupReference; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.common.data.PermissionRule; +import com.google.gwt.core.client.GWT; +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.core.client.Scheduler.ScheduledCommand; +import com.google.gwt.dom.client.DivElement; +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.ValueAwareEditor; +import com.google.gwt.editor.client.adapters.EditorSource; +import com.google.gwt.editor.client.adapters.ListEditor; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.MouseOutEvent; +import com.google.gwt.event.dom.client.MouseOverEvent; +import com.google.gwt.event.logical.shared.CloseEvent; +import com.google.gwt.event.logical.shared.SelectionEvent; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.uibinder.client.UiHandler; +import com.google.gwt.user.client.ui.Anchor; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.CheckBox; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HTMLPanel; +import com.google.gwt.user.client.ui.ValueLabel; + +import java.util.ArrayList; +import java.util.List; + +public class PermissionEditor extends Composite implements Editor<Permission>, + ValueAwareEditor<Permission> { + interface Binder extends UiBinder<HTMLPanel, PermissionEditor> { + } + + private static final Binder uiBinder = GWT.create(Binder.class); + + @UiField(provided = true) + @Path("name") + ValueLabel<String> normalName; + + @UiField(provided = true) + @Path("name") + ValueLabel<String> deletedName; + + @UiField + CheckBox exclusiveGroup; + + @UiField + FlowPanel ruleContainer; + ListEditor<PermissionRule, PermissionRuleEditor> rules; + + @UiField + DivElement addContainer; + @UiField + DivElement addStage1; + @UiField + DivElement addStage2; + @UiField + Anchor beginAddRule; + @UiField + @Editor.Ignore + GroupReferenceBox groupToAdd; + @UiField + Button addRule; + + @UiField + Anchor deletePermission; + + @UiField + DivElement normal; + @UiField + DivElement deleted; + + private final boolean readOnly; + private final AccessSection section; + private Permission value; + private ApprovalType rangeType; + private boolean isDeleted; + + public PermissionEditor(boolean readOnly, AccessSection section) { + this.readOnly = readOnly; + this.section = section; + + normalName = new ValueLabel<String>(PermissionNameRenderer.INSTANCE); + deletedName = new ValueLabel<String>(PermissionNameRenderer.INSTANCE); + + initWidget(uiBinder.createAndBindUi(this)); + rules = ListEditor.of(new RuleEditorSource()); + + exclusiveGroup.setEnabled(!readOnly); + + if (readOnly) { + addContainer.removeFromParent(); + addContainer = null; + + deletePermission.removeFromParent(); + deletePermission = null; + } + } + + @UiHandler("deletePermission") + void onDeleteHover(MouseOverEvent event) { + addStyleName(AdminResources.I.css().deleteSectionHover()); + } + + @UiHandler("deletePermission") + void onDeleteNonHover(MouseOutEvent event) { + removeStyleName(AdminResources.I.css().deleteSectionHover()); + } + + @UiHandler("deletePermission") + void onDeletePermission(ClickEvent event) { + isDeleted = true; + normal.getStyle().setDisplay(Display.NONE); + deleted.getStyle().setDisplay(Display.BLOCK); + } + + @UiHandler("undoDelete") + void onUndoDelete(ClickEvent event) { + isDeleted = false; + deleted.getStyle().setDisplay(Display.NONE); + normal.getStyle().setDisplay(Display.BLOCK); + } + + @UiHandler("beginAddRule") + void onBeginAddRule(ClickEvent event) { + addStage1.getStyle().setDisplay(Display.NONE); + addStage2.getStyle().setDisplay(Display.BLOCK); + + Scheduler.get().scheduleDeferred(new ScheduledCommand() { + @Override + public void execute() { + groupToAdd.setFocus(true); + } + }); + } + + @UiHandler("addRule") + void onAddGroupByClick(ClickEvent event) { + GroupReference ref = groupToAdd.getValue(); + if (ref != null) { + addGroup(ref); + } else { + groupToAdd.setFocus(true); + } + } + + @UiHandler("groupToAdd") + void onAddGroupByEnter(SelectionEvent<GroupReference> event) { + GroupReference ref = event.getSelectedItem(); + if (ref != null) { + addGroup(ref); + } + } + + @UiHandler("groupToAdd") + void onAbortAddGroup(CloseEvent<GroupReferenceBox> event) { + hideAddGroup(); + } + + @UiHandler("hideAddGroup") + void hideAddGroup(ClickEvent event) { + hideAddGroup(); + } + + private void hideAddGroup() { + addStage1.getStyle().setDisplay(Display.BLOCK); + addStage2.getStyle().setDisplay(Display.NONE); + } + + private void addGroup(GroupReference ref) { + 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(); + newRule.setRange(min, max); + } + rules.getList().add(newRule); + } + groupToAdd.setValue(null); + groupToAdd.setFocus(true); + + } else { + // If the oracle didn't get to complete a UUID, resolve it now. + // + addRule.setEnabled(false); + SuggestUtil.SVC.suggestAccountGroup(ref.getName(), 1, + new GerritCallback<List<GroupReference>>() { + @Override + public void onSuccess(List<GroupReference> result) { + addRule.setEnabled(true); + if (result.size() == 1) { + addGroup(result.get(0)); + } else { + groupToAdd.setFocus(true); + } + } + + @Override + public void onFailure(Throwable caught) { + addRule.setEnabled(true); + super.onFailure(caught); + } + }); + } + } + + boolean isDeleted() { + return isDeleted; + } + + @Override + public void setValue(Permission value) { + this.value = value; + if (value.isLabel()) { + rangeType = + Gerrit.getConfig().getApprovalTypes().byLabel(value.getLabel()); + } else { + rangeType = null; + } + + if (value != null && Permission.OWNER.equals(value.getName())) { + exclusiveGroup.setEnabled(false); + } else { + exclusiveGroup.setEnabled(!readOnly); + } + } + + @Override + public void flush() { + List<PermissionRule> src = rules.getList(); + List<PermissionRule> keep = new ArrayList<PermissionRule>(src.size()); + + for (int i = 0; i < src.size(); i++) { + PermissionRuleEditor e = + (PermissionRuleEditor) ruleContainer.getWidget(i); + if (!e.isDeleted()) { + keep.add(src.get(i)); + } + } + value.setRules(keep); + } + + @Override + public void onPropertyChange(String... paths) { + } + + @Override + public void setDelegate(EditorDelegate<Permission> delegate) { + } + + private class RuleEditorSource extends EditorSource<PermissionRuleEditor> { + @Override + public PermissionRuleEditor create(int index) { + PermissionRuleEditor subEditor = + new PermissionRuleEditor(readOnly, section, value, rangeType); + ruleContainer.insert(subEditor, index); + return subEditor; + } + + @Override + public void dispose(PermissionRuleEditor subEditor) { + subEditor.removeFromParent(); + } + + @Override + public void setIndex(PermissionRuleEditor subEditor, int index) { + ruleContainer.insert(subEditor, index); + } + } +}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.ui.xml new file mode 100644 index 0000000..25995d9 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionEditor.ui.xml
@@ -0,0 +1,145 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +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. +--> +<ui:UiBinder + xmlns:ui='urn:ui:com.google.gwt.uibinder' + xmlns:g='urn:import:com.google.gwt.user.client.ui' + xmlns:e='urn:import:com.google.gwt.editor.ui.client' + xmlns:my='urn:import:com.google.gerrit.client.admin' + ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat' + ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator' + ui:generateLocales='default,en' + > +<ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/> +<ui:style> + @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor; + @eval backgroundColor com.google.gerrit.client.Gerrit.getTheme().backgroundColor; + + .panel { + position: relative; + } + + .normal { + border: 1px solid backgroundColor; + margin-top: -1px; + margin-bottom: -1px; + } + + .header { + padding-left: 5px; + padding-right: 5px; + padding-bottom: 1px; + white-space: nowrap; + } + + .header:hover { + background-color: selectionColor; + } + + .name { + font-style: italic; + } + + .exclusiveGroup { + position: absolute; + top: 0; + right: 36px; + width: 7em; + font-size: 80%; + } + + .addContainer { + padding-left: 10px; + position: relative; + } + .addContainer:hover { + background-color: selectionColor; + } + .addLink { + font-size: 80%; + } + + .deleteIcon { + position: absolute; + top: 1px; + right: 12px; + } +</ui:style> + +<g:HTMLPanel stylePrimaryName='{style.panel}'> +<div ui:field='normal' class='{style.normal}'> + <div class='{style.header}'> + <g:ValueLabel styleName='{style.name}' ui:field='normalName'/> + <g:CheckBox + ui:field='exclusiveGroup' + addStyleNames='{style.exclusiveGroup}' + text='Exclusive'> + <ui:attribute name='text'/> + </g:CheckBox> + <g:Anchor + ui:field='deletePermission' + href='javascript:void' + styleName='{style.deleteIcon} {res.css.deleteIcon}' + title='Delete this permission (and nested rules)'> + <ui:attribute name='title'/> + </g:Anchor> + </div> + <g:FlowPanel ui:field='ruleContainer'/> + <div ui:field='addContainer' class='{style.addContainer}'> + <div ui:field='addStage1'> + <g:Anchor + ui:field='beginAddRule' + styleName='{style.addLink}' + href='javascript:void' + text='Add Group'> + <ui:attribute name='text'/> + </g:Anchor> + </div> + <div ui:field='addStage2' style='display: none'> + <ui:msg>Group Name: <my:GroupReferenceBox + ui:field='groupToAdd' + visibleLength='45'/></ui:msg> + <g:Button + ui:field='addRule' + text='Add'> + <ui:attribute name='text'/> + </g:Button> + <g:Anchor + ui:field='hideAddGroup' + href='javascript:void' + styleName='{style.deleteIcon} {res.css.deleteIcon}' + title='Cancel additional group'> + <ui:attribute name='title'/> + </g:Anchor> + </div> + </div> +</div> + +<div + ui:field='deleted' + class='{res.css.deleted} {res.css.deletedBorder}' + style='display: none'> + <ui:msg>Permission <g:ValueLabel styleName='{style.name}' ui:field='deletedName'/> was deleted</ui:msg> + <g:Anchor + ui:field='undoDelete' + href='javascript:void' + styleName='{style.deleteIcon} {res.css.undoIcon}' + title='Undo deletion'> + <ui:attribute name='title'/> + </g:Anchor> +</div> +</g:HTMLPanel> +</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionNameRenderer.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionNameRenderer.java new file mode 100644 index 0000000..1dea1fb --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionNameRenderer.java
@@ -0,0 +1,53 @@ +// 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.gerrit.common.data.Permission; +import com.google.gwt.text.shared.Renderer; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +class PermissionNameRenderer implements Renderer<String> { + static final PermissionNameRenderer INSTANCE = new PermissionNameRenderer(); + + private static Map<String, String> LC; + + @Override + public String render(String varName) { + if (Permission.isLabel(varName)) { + return Util.M.label(new Permission(varName).getLabel()); + } + + Map<String, String> m = Util.C.permissionNames(); + String desc = m.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()); + } + return desc != null ? desc : varName; + } + + @Override + public void render(String object, Appendable appendable) throws IOException { + appendable.append(render(object)); + } +} \ No newline at end of file
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java new file mode 100644 index 0000000..12b71c985 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.java
@@ -0,0 +1,200 @@ +// 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 static com.google.gerrit.common.data.Permission.PUSH; +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.PermissionRule; +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.DivElement; +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.ValueAwareEditor; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.text.shared.Renderer; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.uibinder.client.UiHandler; +import com.google.gwt.user.client.DOM; +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.ValueListBox; + +import java.io.IOException; +import java.util.Arrays; + +public class PermissionRuleEditor extends Composite implements + Editor<PermissionRule>, ValueAwareEditor<PermissionRule> { + interface Binder extends UiBinder<HTMLPanel, PermissionRuleEditor> { + } + + private static final Binder uiBinder = GWT.create(Binder.class); + + @UiField(provided = true) + ValueListBox<PermissionRule.Action> action; + + @UiField(provided = true) + ValueListBox<Integer> min; + + @UiField(provided = true) + ValueListBox<Integer> max; + + @UiField + CheckBox force; + + @UiField + Hyperlink normalGroupName; + @UiField + SpanElement deletedGroupName; + + @UiField + Anchor deleteRule; + + @UiField + DivElement normal; + @UiField + DivElement deleted; + + @UiField + SpanElement rangeEditor; + + private boolean isDeleted; + + public PermissionRuleEditor(boolean readOnly, AccessSection section, + Permission permission, ApprovalType labelRange) { + 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()); + + min.setAcceptableValues(labelRange.getValuesAsList()); + max.setAcceptableValues(labelRange.getValuesAsList()); + } else { + action.setValue(PermissionRule.Action.ALLOW); + action.setAcceptableValues(Arrays.asList(PermissionRule.Action.values())); + } + + initWidget(uiBinder.createAndBindUi(this)); + + String name = permission.getName(); + boolean canForce = PUSH.equals(name) || PUSH_TAG.equals(name); + if (canForce) { + String ref = section.getRefPattern(); + canForce = !ref.startsWith("refs/for/") && !ref.startsWith("^refs/for/"); + } + force.setVisible(canForce); + force.setEnabled(!readOnly); + + if (labelRange != null) { + 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); + } + + if (readOnly) { + deleteRule.removeFromParent(); + deleteRule = null; + } + } + + boolean isDeleted() { + return isDeleted; + } + + @UiHandler("deleteRule") + void onDeleteRule(ClickEvent event) { + isDeleted = true; + normal.getStyle().setDisplay(Display.NONE); + deleted.getStyle().setDisplay(Display.BLOCK); + } + + @UiHandler("undoDelete") + void onUndoDelete(ClickEvent event) { + isDeleted = false; + deleted.getStyle().setDisplay(Display.NONE); + normal.getStyle().setDisplay(Display.BLOCK); + } + + @Override + public void setValue(PermissionRule value) { + GroupReference ref = value.getGroup(); + normalGroupName.setTargetHistoryToken(Dispatcher.toGroup(ref.getUUID())); + normalGroupName.setText(ref.getName()); + deletedGroupName.setInnerText(ref.getName()); + } + + @Override + public void setDelegate(EditorDelegate<PermissionRule> delegate) { + } + + @Override + public void flush() { + } + + @Override + public void onPropertyChange(String... paths) { + } + + private static class ActionRenderer implements + Renderer<PermissionRule.Action> { + @Override + public String render(PermissionRule.Action object) { + return object != null ? object.toString() : ""; + } + + @Override + public void render(PermissionRule.Action object, Appendable appendable) + throws IOException { + appendable.append(render(object)); + } + } + + 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(); +}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml new file mode 100644 index 0000000..447c99f --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/PermissionRuleEditor.ui.xml
@@ -0,0 +1,112 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +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. +--> +<ui:UiBinder + xmlns:ui='urn:ui:com.google.gwt.uibinder' + xmlns:g='urn:import:com.google.gwt.user.client.ui' + xmlns:e='urn:import:com.google.gwt.editor.ui.client' + xmlns:my='urn:import:com.google.gerrit.client.admin' + xmlns:q='urn:import:com.google.gerrit.client.ui' + ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat' + ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator' + ui:generateLocales='default,en' + > +<ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/> +<ui:style> + @eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor; + + .panel { + position: relative; + height: 1.5em; + } + + .panel:hover { + background-color: selectionColor; + } + + .normal { + padding-left: 10px; + white-space: nowrap; + height: 100%; + } + + .deleted { + height: 100%; + } + + .actionList, .minmax { + font-size: 80%; + } + + .forcePush { + position: absolute; + top: 0; + right: 36px; + width: 7em; + font-size: 80%; + } + + .deleteIcon { + position: absolute; + top: 2px; + right: 11px; + } + + .groupName { + display: inline; + } +</ui:style> + +<g:HTMLPanel styleName='{style.panel}'> +<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}'/> + </span> + + <q:Hyperlink ui:field='normalGroupName' styleName='{style.groupName}'/> + <g:CheckBox + ui:field='force' + addStyleNames='{style.forcePush}' + text='Force Push'> + <ui:attribute name='text'/> + </g:CheckBox> + + <g:Anchor + ui:field='deleteRule' + href='javascript:void' + styleName='{style.deleteIcon} {res.css.deleteIcon}' + title='Delete this rule'> + <ui:attribute name='title'/> + </g:Anchor> +</div> + +<div + ui:field='deleted' + class='{res.css.deleted} {style.deleted}' + style='display: none'> + <ui:msg>Group <span ui:field='deletedGroupName'/> was deleted</ui:msg> + <g:Anchor + ui:field='undoDelete' + href='javascript:void' + styleName='{style.deleteIcon} {res.css.undoIcon}' + title='Undo deletion'> + <ui:attribute name='title'/> + </g:Anchor> +</div> +</g:HTMLPanel> +</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java new file mode 100644 index 0000000..c550d8c --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.java
@@ -0,0 +1,150 @@ +// 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.gerrit.client.Dispatcher; +import com.google.gerrit.client.ui.Hyperlink; +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; +import com.google.gwt.dom.client.DivElement; +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.ValueAwareEditor; +import com.google.gwt.editor.client.adapters.EditorSource; +import com.google.gwt.editor.client.adapters.ListEditor; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.uibinder.client.UiHandler; +import com.google.gwt.user.client.ui.Anchor; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HTMLPanel; + +import java.util.ArrayList; +import java.util.List; + +public class ProjectAccessEditor extends Composite implements + Editor<ProjectAccess>, ValueAwareEditor<ProjectAccess> { + interface Binder extends UiBinder<HTMLPanel, ProjectAccessEditor> { + } + + private static final Binder uiBinder = GWT.create(Binder.class); + + @UiField + DivElement inheritsFrom; + + @UiField + Hyperlink parentProject; + + @UiField + FlowPanel localContainer; + ListEditor<AccessSection, AccessSectionEditor> local; + + @UiField + Anchor addSection; + + private ProjectAccess value; + + private boolean editing; + + public ProjectAccessEditor() { + initWidget(uiBinder.createAndBindUi(this)); + local = ListEditor.of(new Source(localContainer)); + } + + @UiHandler("addSection") + void onAddSection(ClickEvent event) { + int index = local.getList().size(); + local.getList().add(new AccessSection("refs/heads/*")); + + AccessSectionEditor editor = local.getEditors().get(index); + editor.enableEditing(); + editor.editRefPattern(); + } + + @Override + public void setValue(ProjectAccess value) { + this.value = value; + + Project.NameKey parent = value.getInheritsFrom(); + if (parent != null) { + inheritsFrom.getStyle().setDisplay(Display.BLOCK); + parentProject.setText(parent.get()); + parentProject.setTargetHistoryToken( // + Dispatcher.toProjectAdmin(parent, ProjectScreen.ACCESS)); + } else { + inheritsFrom.getStyle().setDisplay(Display.NONE); + } + + addSection.setVisible(value != null && editing && !value.getOwnerOf().isEmpty()); + } + + @Override + public void flush() { + List<AccessSection> src = local.getList(); + List<AccessSection> keep = new ArrayList<AccessSection>(src.size()); + + for (int i = 0; i < src.size(); i++) { + AccessSectionEditor e = (AccessSectionEditor) localContainer.getWidget(i); + if (!e.isDeleted()) { + keep.add(src.get(i)); + } + } + value.setLocal(keep); + } + + @Override + public void onPropertyChange(String... paths) { + } + + @Override + public void setDelegate(EditorDelegate<ProjectAccess> delegate) { + } + + void setEditing(final boolean editing) { + this.editing = editing; + addSection.setVisible(editing); + } + + private class Source extends EditorSource<AccessSectionEditor> { + private final FlowPanel container; + + Source(FlowPanel container) { + this.container = container; + } + + @Override + public AccessSectionEditor create(int index) { + AccessSectionEditor subEditor = new AccessSectionEditor(value); + subEditor.setEditing(editing); + container.insert(subEditor, index); + return subEditor; + } + + @Override + public void dispose(AccessSectionEditor subEditor) { + subEditor.removeFromParent(); + } + + @Override + public void setIndex(AccessSectionEditor subEditor, int index) { + container.insert(subEditor, index); + } + } +}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml new file mode 100644 index 0000000..9360daa --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessEditor.ui.xml
@@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +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. +--> +<ui:UiBinder + xmlns:ui='urn:ui:com.google.gwt.uibinder' + xmlns:g='urn:import:com.google.gwt.user.client.ui' + xmlns:q='urn:import:com.google.gerrit.client.ui' + ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat' + ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator' + ui:generateLocales='default,en' + > +<ui:style> + .inheritsFrom { + margin-bottom: 0.5em; + } + .parentTitle { + font-weight: bold; + } + .parentLink { + display: inline; + } + + .addContainer { + margin-top: 5px; + font-size: 80%; + } + .addContainer:hover { + background-color: selectionColor; + } +</ui:style> + +<g:HTMLPanel> + <div ui:field='inheritsFrom' class='{style.inheritsFrom}'> + <span class='{style.parentTitle}'><ui:msg>Rights Inherit From:</ui:msg></span> + <q:Hyperlink ui:field='parentProject' styleName='{style.parentLink}'/> + </div> + + <g:FlowPanel ui:field='localContainer'/> + <div class='{style.addContainer}'> + <g:Anchor + ui:field='addSection' + href='javascript:void' + text='Add Reference'> + <ui:attribute name='text'/> + </g:Anchor> + </div> +</g:HTMLPanel> +</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java index b87f6f1..e9d3889 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.java
@@ -1,4 +1,4 @@ -// Copyright (C) 2008 The Android Open Source Project +// 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. @@ -14,46 +14,60 @@ package com.google.gerrit.client.admin; -import com.google.gerrit.client.Dispatcher; import com.google.gerrit.client.Gerrit; import com.google.gerrit.client.rpc.GerritCallback; import com.google.gerrit.client.rpc.ScreenLoadCallback; -import com.google.gerrit.client.ui.FancyFlexTable; -import com.google.gerrit.client.ui.Hyperlink; -import com.google.gerrit.client.ui.SmallHeading; -import com.google.gerrit.common.data.ApprovalType; -import com.google.gerrit.common.data.GerritConfig; -import com.google.gerrit.common.data.InheritedRefRight; -import com.google.gerrit.common.data.ProjectDetail; -import com.google.gerrit.reviewdb.AccountGroup; -import com.google.gerrit.reviewdb.ApprovalCategoryValue; +import com.google.gerrit.common.PageLinks; +import com.google.gerrit.common.data.ProjectAccess; import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.RefRight; +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.editor.client.SimpleBeanEditorDriver; import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.event.dom.client.ClickHandler; -import com.google.gwt.event.logical.shared.ValueChangeEvent; -import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.uibinder.client.UiHandler; +import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.CheckBox; -import com.google.gwt.user.client.ui.Grid; -import com.google.gwt.user.client.ui.Panel; -import com.google.gwt.user.client.ui.VerticalPanel; -import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter; -import com.google.gwtexpui.safehtml.client.SafeHtml; -import com.google.gwtexpui.safehtml.client.SafeHtmlBuilder; - -import java.util.HashSet; -import java.util.List; -import java.util.Map; +import com.google.gwt.user.client.ui.HTMLPanel; +import com.google.gwt.user.client.ui.UIObject; +import com.google.gwtexpui.globalkey.client.NpTextArea; public class ProjectAccessScreen extends ProjectScreen { - private Panel parentPanel; - private Hyperlink parentName; + interface Binder extends UiBinder<HTMLPanel, ProjectAccessScreen> { + } - private RightsTable rights; - private Button delRight; - private AccessRightEditor rightEditor; - private CheckBox showInherited; + private static final Binder uiBinder = GWT.create(Binder.class); + + interface Driver extends SimpleBeanEditorDriver< // + ProjectAccess, // + ProjectAccessEditor> { + } + + @UiField + DivElement editTools; + + @UiField + Button edit; + + @UiField + Button cancel; + + @UiField + ProjectAccessEditor accessEditor; + + @UiField + DivElement commitTools; + + @UiField + NpTextArea commitMessage; + + @UiField + Button commit; + + private Driver driver; + + private ProjectAccess access; public ProjectAccessScreen(final Project.NameKey toShow) { super(toShow); @@ -62,266 +76,87 @@ @Override protected void onInitUI() { super.onInitUI(); - initParent(); - initRights(); + add(uiBinder.createAndBindUi(this)); + + driver = GWT.create(Driver.class); + accessEditor.setEditing(false); + driver.initialize(accessEditor); } @Override protected void onLoad() { super.onLoad(); - Util.PROJECT_SVC.projectDetail(getProjectKey(), - new ScreenLoadCallback<ProjectDetail>(this) { - public void preDisplay(final ProjectDetail result) { - enableForm(true); - display(result); + Util.PROJECT_SVC.projectAccess(getProjectKey(), + new ScreenLoadCallback<ProjectAccess>(this) { + @Override + public void preDisplay(ProjectAccess access) { + edit(access); } }); } - private void enableForm(final boolean on) { - delRight.setEnabled(on); - rightEditor.enableForm(on); + void edit(ProjectAccess access) { + this.access = access; + final boolean editing = !edit.isEnabled(); + accessEditor.setEditing(editing); + UIObject.setVisible(editTools, !access.getOwnerOf().isEmpty()); + cancel.setVisible(editing); + UIObject.setVisible(commitTools, editing); + driver.edit(access); } - private void initParent() { - parentName = new Hyperlink("", ""); - - showInherited = new CheckBox(); - showInherited.setValue(true); - showInherited.addClickHandler(new ClickHandler() { - public void onClick(ClickEvent event) { - rights.showInherited(showInherited.getValue()); - } - }); - - Grid g = new Grid(2, 3); - g.setWidget(0, 0, new SmallHeading(Util.C.headingParentProjectName())); - g.setWidget(1, 0, parentName); - g.setWidget(1, 1, showInherited); - g.setText(1, 2, Util.C.headingShowInherited()); - - parentPanel = new VerticalPanel(); - parentPanel.add(g); - add(parentPanel); + @UiHandler("edit") + void onEdit(ClickEvent event) { + edit.setEnabled(false); + cancel.setVisible(true); + UIObject.setVisible(commitTools, true); + accessEditor.setEditing(true); + driver.edit(access); } - private void initRights() { - rights = new RightsTable(); - - delRight = new Button(Util.C.buttonDeleteGroupMembers()); - delRight.addClickHandler(new ClickHandler() { - @Override - public void onClick(final ClickEvent event) { - final HashSet<RefRight.Key> refRightIds = rights.getRefRightIdsChecked(); - doDeleteRefRights(refRightIds); - } - }); - - rightEditor = new AccessRightEditor(getProjectKey()); - rightEditor.addValueChangeHandler(new ValueChangeHandler<ProjectDetail>() { - @Override - public void onValueChange(ValueChangeEvent<ProjectDetail> event) { - display(event.getValue()); - } - }); - - add(new SmallHeading(Util.C.headingAccessRights())); - add(rights); - add(delRight); - add(rightEditor); + @UiHandler(value={"cancel", "cancel2"}) + void onCancel(ClickEvent event) { + Gerrit.display(PageLinks.toProjectAcceess(getProjectKey())); } - void display(final ProjectDetail result) { - final Project project = result.project; + @UiHandler("commit") + void onCommit(ClickEvent event) { + ProjectAccess access = driver.flush(); - final Project.NameKey wildKey = Gerrit.getConfig().getWildProject(); - final boolean isWild = wildKey.equals(project.getNameKey()); - Project.NameKey parent = project.getParent(); - if (parent == null) { - parent = wildKey; + if (driver.hasErrors()) { + Window.alert(Util.C.errorsMustBeFixed()); + return; } - parentPanel.setVisible(!isWild); - parentName.setTargetHistoryToken(Dispatcher.toProjectAdmin(parent, ACCESS)); - parentName.setText(parent.get()); + String message = commitMessage.getText().trim(); + if ("".equals(message)) { + message = null; + } - rights.display(result.groups, result.rights); + enable(false); + Util.PROJECT_SVC.changeProjectAccess( // + getProjectKey(), // + access.getRevision(), // + message, // + access.getLocal(), // + new GerritCallback<ProjectAccess>() { + @Override + public void onSuccess(ProjectAccess access) { + commitMessage.setText(""); + edit(access); + enable(true); + } - rightEditor.setVisible(result.canModifyAccess); - delRight.setVisible(rights.getCanDelete()); + @Override + public void onFailure(Throwable caught) { + enable(true); + super.onFailure(caught); + } + }); } - private void doDeleteRefRights(final HashSet<RefRight.Key> refRightIds) { - if (!refRightIds.isEmpty()) { - Util.PROJECT_SVC.deleteRight(getProjectKey(), refRightIds, - new GerritCallback<ProjectDetail>() { - @Override - public void onSuccess(final ProjectDetail result) { - //The user could no longer modify access after deleting a ref right. - display(result); - } - }); - } - } - - private class RightsTable extends FancyFlexTable<InheritedRefRight> { - boolean canDelete; - Map<AccountGroup.Id, AccountGroup> groups; - - RightsTable() { - table.setWidth(""); - table.setText(0, 2, Util.C.columnRightOrigin()); - table.setText(0, 3, Util.C.columnApprovalCategory()); - table.setText(0, 4, Util.C.columnGroupName()); - table.setText(0, 5, Util.C.columnRefName()); - table.setText(0, 6, Util.C.columnRightRange()); - - final FlexCellFormatter fmt = table.getFlexCellFormatter(); - fmt.addStyleName(0, 1, Gerrit.RESOURCES.css().iconHeader()); - fmt.addStyleName(0, 2, Gerrit.RESOURCES.css().dataHeader()); - fmt.addStyleName(0, 3, Gerrit.RESOURCES.css().dataHeader()); - fmt.addStyleName(0, 4, Gerrit.RESOURCES.css().dataHeader()); - fmt.addStyleName(0, 5, Gerrit.RESOURCES.css().dataHeader()); - fmt.addStyleName(0, 6, Gerrit.RESOURCES.css().dataHeader()); - - table.addClickHandler(new ClickHandler() { - @Override - public void onClick(final ClickEvent event) { - onOpenRow(table.getCellForEvent(event).getRowIndex()); - } - }); - } - - HashSet<RefRight.Key> getRefRightIdsChecked() { - final HashSet<RefRight.Key> refRightIds = new HashSet<RefRight.Key>(); - for (int row = 1; row < table.getRowCount(); row++) { - RefRight r = getRowItem(row).getRight(); - if (r != null && table.getWidget(row, 1) instanceof CheckBox - && ((CheckBox) table.getWidget(row, 1)).getValue()) { - refRightIds.add(r.getKey()); - } - } - return refRightIds; - } - - void display(final Map<AccountGroup.Id, AccountGroup> grps, - final List<InheritedRefRight> refRights) { - groups = grps; - canDelete = false; - - while (1 < table.getRowCount()) - table.removeRow(table.getRowCount() - 1); - - for (final InheritedRefRight r : refRights) { - final int row = table.getRowCount(); - table.insertRow(row); - if (! showInherited.getValue() && r.isInherited()) { - table.getRowFormatter().setVisible(row, false); - } - applyDataRowStyle(row); - populate(row, r); - } - } - - protected void onOpenRow(final int row) { - if (row > 0) { - RefRight right = getRowItem(row).getRight(); - rightEditor.load(right, groups.get(right.getAccountGroupId())); - } - } - - void populate(final int row, final InheritedRefRight r) { - final GerritConfig config = Gerrit.getConfig(); - final RefRight right = r.getRight(); - final ApprovalType ar = - config.getApprovalTypes().getApprovalType( - right.getApprovalCategoryId()); - final AccountGroup group = groups.get(right.getAccountGroupId()); - - if (r.isInherited() || !r.isOwner()) { - table.setText(row, 1, ""); - } else { - table.setWidget(row, 1, new CheckBox()); - canDelete = true; - } - - if (r.isInherited()) { - Project.NameKey fromProject = right.getKey().getProjectNameKey(); - table.setWidget(row, 2, new Hyperlink(fromProject.get(), Dispatcher - .toProjectAdmin(fromProject, ACCESS))); - } else { - table.setText(row, 2, ""); - } - - table.setText(row, 3, ar != null ? ar.getCategory().getName() - : right.getApprovalCategoryId().get() ); - - if (group != null) { - table.setWidget(row, 4, new Hyperlink(group.getName(), Dispatcher - .toAccountGroup(group.getId()))); - } else { - table.setText(row, 4, Util.M.deletedGroup(right.getAccountGroupId() - .get())); - } - - table.setText(row, 5, right.getRefPatternForDisplay()); - - { - final SafeHtmlBuilder m = new SafeHtmlBuilder(); - final ApprovalCategoryValue min, max; - min = ar != null ? ar.getValue(right.getMinValue()) : null; - max = ar != null ? ar.getValue(right.getMaxValue()) : null; - - if (ar != null && ar.getCategory().isRange()) { - formatValue(m, right.getMinValue(), min); - m.br(); - } - formatValue(m, right.getMaxValue(), max); - SafeHtml.set(table, row, 6, m); - } - - final FlexCellFormatter fmt = table.getFlexCellFormatter(); - fmt.addStyleName(row, 1, Gerrit.RESOURCES.css().iconCell()); - fmt.addStyleName(row, 2, Gerrit.RESOURCES.css().dataCell()); - fmt.addStyleName(row, 3, Gerrit.RESOURCES.css().dataCell()); - fmt.addStyleName(row, 4, Gerrit.RESOURCES.css().dataCell()); - fmt.addStyleName(row, 5, Gerrit.RESOURCES.css().dataCell()); - fmt.addStyleName(row, 6, Gerrit.RESOURCES.css().dataCell()); - fmt.addStyleName(row, 6, Gerrit.RESOURCES.css() - .projectAdminApprovalCategoryRangeLine()); - - setRowItem(row, r); - } - - public void showInherited(boolean visible) { - for (int r = 0; r < table.getRowCount(); r++) { - if (getRowItem(r) != null && getRowItem(r).isInherited()) { - table.getRowFormatter().setVisible(r, visible); - } - } - } - - private void formatValue(final SafeHtmlBuilder m, final short v, - final ApprovalCategoryValue e) { - m.openSpan(); - m - .setStyleName(Gerrit.RESOURCES.css() - .projectAdminApprovalCategoryValue()); - if (v == 0) { - m.append(' '); - } else if (v > 0) { - m.append('+'); - } - m.append(v); - m.closeSpan(); - if (e != null) { - m.append(": "); - m.append(e.getName()); - } - } - - private boolean getCanDelete() { - return canDelete; - } + private void enable(boolean enabled) { + commitMessage.setEnabled(enabled); + commit.setEnabled(enabled); } }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.ui.xml new file mode 100644 index 0000000..9a74610 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectAccessScreen.ui.xml
@@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +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. +--> +<ui:UiBinder + xmlns:ui='urn:ui:com.google.gwt.uibinder' + xmlns:g='urn:import:com.google.gwt.user.client.ui' + xmlns:my='urn:import:com.google.gerrit.client.admin' + xmlns:expui='urn:import:com.google.gwtexpui.globalkey.client' + ui:generateFormat='com.google.gwt.i18n.rebind.format.PropertiesFormat' + ui:generateKeys='com.google.gwt.i18n.rebind.keygen.MD5KeyGenerator' + ui:generateLocales='default,en' + > +<ui:style> + @external .gwt-TextArea; + + .commitMessage { + margin-top: 2em; + } + .commitMessage .gwt-TextArea { + margin: 5px 5px 5px 5px; + } +</ui:style> + +<g:HTMLPanel> + <div ui:field='editTools'> + <g:Button + ui:field='edit' + text='Edit'> + <ui:attribute name='text'/> + </g:Button> + <g:Button + ui:field='cancel' + text='Cancel'> + <ui:attribute name='text'/> + </g:Button> + </div> + <my:ProjectAccessEditor ui:field='accessEditor'/> + <div ui:field='commitTools'> + <div class='{style.commitMessage}'> + <ui:msg>Commit Message (optional):</ui:msg><br/> + <expui:NpTextArea + ui:field='commitMessage' + visibleLines='4' + characterWidth='60' + spellCheck='true' + /> + </div> + <g:Button + ui:field='commit' + text='Save Changes'> + <ui:attribute name='text'/> + </g:Button> + <g:Button + ui:field='cancel2' + text='Cancel'> + <ui:attribute name='text'/> + </g:Button> + </div> + <div style='width: 35em; visibility: hidden;' /> +</g:HTMLPanel> +</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/RefPatternBox.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/RefPatternBox.java new file mode 100644 index 0000000..a0a3d17 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/RefPatternBox.java
@@ -0,0 +1,89 @@ +// 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.dom.client.Document; +import com.google.gwt.event.dom.client.KeyPressEvent; +import com.google.gwt.event.dom.client.KeyPressHandler; +import com.google.gwt.text.shared.Parser; +import com.google.gwt.text.shared.Renderer; +import com.google.gwt.user.client.ui.ValueBox; +import com.google.gwtexpui.globalkey.client.GlobalKey; + +import java.io.IOException; +import java.text.ParseException; + +public class RefPatternBox extends ValueBox<String> { + private static final Renderer<String> RENDERER = new Renderer<String>() { + public String render(String ref) { + return ref; + } + + public void render(String ref, Appendable dst) throws IOException { + dst.append(render(ref)); + } + }; + + private static final Parser<String> PARSER = new Parser<String>() { + public String parse(CharSequence text) throws ParseException { + String ref = text.toString(); + + if (ref.isEmpty()) { + throw new ParseException(Util.C.refErrorEmpty(), 0); + } + + if (ref.charAt(0) == '/') { + throw new ParseException(Util.C.refErrorBeginSlash(), 0); + } + + final boolean re = ref.charAt(0) == '^'; + if (re && !ref.startsWith("^refs/")) { + ref = "^refs/heads/" + ref.substring(1); + } else if (!ref.startsWith("refs/")) { + ref = "refs/heads/" + ref; + } + + for (int i = 0; i < ref.length(); i++) { + final char c = ref.charAt(i); + + if (c == '/' && 0 < i && ref.charAt(i - 1) == '/') { + throw new ParseException(Util.C.refErrorDoubleSlash(), i); + } + + if (c == ' ') { + throw new ParseException(Util.C.refErrorNoSpace(), i); + } + + if (c < ' ') { + throw new ParseException(Util.C.refErrorPrintable(), i); + } + } + return ref; + } + }; + + public RefPatternBox() { + super(Document.get().createTextInputElement(), RENDERER, PARSER); + addKeyPressHandler(GlobalKey.STOP_PROPAGATION); + addKeyPressHandler(new KeyPressHandler() { + @Override + public void onKeyPress(KeyPressEvent event) { + if (event.getCharCode() == ' ') { + event.preventDefault(); + } + } + }); + } +}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/Util.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/Util.java index 4167116..c599ee9 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/Util.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/Util.java
@@ -32,6 +32,8 @@ PROJECT_SVC = GWT.create(ProjectAdminService.class); JsonUtil.bind(PROJECT_SVC, "rpc/ProjectAdminService"); + + AdminResources.I.css().ensureInjected(); } public static String toLongString(final Project.SubmitType type) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.java new file mode 100644 index 0000000..d8b8c5d --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.java
@@ -0,0 +1,206 @@ +// 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.core.client.GWT; +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.NativeEvent; +import com.google.gwt.dom.client.Style.Display; +import com.google.gwt.editor.client.EditorError; +import com.google.gwt.editor.client.HasEditorErrors; +import com.google.gwt.editor.client.IsEditor; +import com.google.gwt.editor.client.LeafValueEditor; +import com.google.gwt.editor.ui.client.adapters.ValueBoxEditor; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.dom.client.DoubleClickEvent; +import com.google.gwt.event.dom.client.DoubleClickHandler; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiChild; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.Focusable; +import com.google.gwt.user.client.ui.Image; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.ValueBoxBase; +import com.google.gwt.user.client.ui.Widget; + +import java.text.ParseException; +import java.util.List; + +public class ValueEditor<T> extends Composite implements HasEditorErrors<T>, + IsEditor<ValueBoxEditor<T>>, LeafValueEditor<T>, Focusable { + interface Binder extends UiBinder<Widget, ValueEditor<?>> { + } + + static final Binder uiBinder = GWT.create(Binder.class); + + @UiField + SimplePanel textPanel; + private Label textLabel; + private StartEditHandlers startHandlers; + + @UiField + Image editIcon; + + @UiField + SimplePanel editPanel; + + @UiField + DivElement errorLabel; + + private ValueBoxBase<T> editChild; + private ValueBoxEditor<T> editProxy; + + public ValueEditor() { + startHandlers = new StartEditHandlers(); + initWidget(uiBinder.createAndBindUi(this)); + editPanel.setVisible(false); + editIcon.addClickHandler(startHandlers); + } + + public void edit() { + textPanel.removeFromParent(); + textPanel = null; + textLabel = null; + + editIcon.removeFromParent(); + editIcon = null; + startHandlers = null; + + editPanel.setVisible(true); + } + + public ValueBoxEditor<T> asEditor() { + if (editProxy == null) { + editProxy = new EditorProxy(); + } + return editProxy; + } + + @Override + public T getValue() { + return asEditor().getValue(); + } + + @Override + public void setValue(T value) { + asEditor().setValue(value); + } + + public void setEditTitle(String title) { + editIcon.setTitle(title); + } + + @UiChild(limit = 1, tagname = "display") + public void setDisplay(Label widget) { + textLabel = widget; + textPanel.add(textLabel); + + textLabel.addClickHandler(startHandlers); + textLabel.addDoubleClickHandler(startHandlers); + } + + @UiChild(limit = 1, tagname = "editor") + public void setEditor(ValueBoxBase<T> widget) { + editChild = widget; + editPanel.add(editChild); + editProxy = null; + } + + public void setEnabled(boolean enabled) { + editIcon.setVisible(enabled); + startHandlers.enabled = enabled; + } + + public void showErrors(List<EditorError> errors) { + StringBuilder buf = new StringBuilder(); + for (EditorError error : errors) { + if (error.getEditor().equals(editProxy)) { + buf.append("\n"); + if (error.getUserData() instanceof ParseException) { + buf.append(((ParseException) error.getUserData()).getMessage()); + } else { + buf.append(error.getMessage()); + } + } + } + + if (0 < buf.length()) { + errorLabel.setInnerText(buf.substring(1)); + errorLabel.getStyle().setDisplay(Display.BLOCK); + } else { + errorLabel.setInnerText(""); + errorLabel.getStyle().setDisplay(Display.NONE); + } + } + + @Override + public void setAccessKey(char key) { + editChild.setAccessKey(key); + } + + @Override + public void setFocus(boolean focused) { + editChild.setFocus(focused); + if (focused) { + editChild.setCursorPos(editChild.getText().length()); + } + } + + @Override + public int getTabIndex() { + return editChild.getTabIndex(); + } + + @Override + public void setTabIndex(int index) { + editChild.setTabIndex(index); + } + + private class StartEditHandlers implements ClickHandler, DoubleClickHandler { + boolean enabled; + + @Override + public void onClick(ClickEvent event) { + if (enabled && event.getNativeButton() == NativeEvent.BUTTON_LEFT) { + edit(); + } + } + + @Override + public void onDoubleClick(DoubleClickEvent event) { + if (enabled && event.getNativeButton() == NativeEvent.BUTTON_LEFT) { + edit(); + } + } + } + + private class EditorProxy extends ValueBoxEditor<T> { + EditorProxy() { + super(editChild); + } + + @Override + public void setValue(T value) { + super.setValue(value); + if (textLabel == null) { + setDisplay(new Label()); + } + textLabel.setText(editChild.getText()); + } + } +}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.ui.xml b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.ui.xml new file mode 100644 index 0000000..98628480 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ValueEditor.ui.xml
@@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- +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. +--> +<ui:UiBinder + xmlns:ui='urn:ui:com.google.gwt.uibinder' + xmlns:g='urn:import:com.google.gwt.user.client.ui' + > +<ui:with field='res' type='com.google.gerrit.client.admin.AdminResources'/> +<ui:style> + .panel { + position: relative; + white-space: nowrap; + } + + .textPanel { + width: 100%; + padding-right: 21px; + } + + .editIcon { + position: absolute; + top: 0; + right: 5px; + } + + .editPanel { + width: 100%; + } + + .errorLabel { + display: none; + color: red; + white-space: pre; + } +</ui:style> +<g:HTMLPanel stylePrimaryName='{style.panel}'> + <g:Image + ui:field='editIcon' + resource='{res.editText}' + stylePrimaryName='{style.editIcon}' + title='Edit'> + <ui:attribute name='title'/> + </g:Image> + <g:SimplePanel ui:field='textPanel' stylePrimaryName='{style.textPanel}'/> + + <g:SimplePanel ui:field='editPanel' stylePrimaryName='{style.editPanel}'/> + <div + ui:field='errorLabel' + class='{style.errorLabel}'/> +</g:HTMLPanel> +</ui:UiBinder>
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/admin.css b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/admin.css new file mode 100644 index 0000000..eca4823 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/admin.css
@@ -0,0 +1,53 @@ +/* Copyright (C) 2009 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. + */ + +@eval selectionColor com.google.gerrit.client.Gerrit.getTheme().selectionColor; +@eval textColor com.google.gerrit.client.Gerrit.getTheme().textColor; +@def deletedBackground #a9a9a9; + +@sprite .deleteIcon { + gwt-image: 'deleteNormal'; + border: none; +} + +@sprite .deleteIcon:hover { + gwt-image: 'deleteHover'; + border: none; +} + +@sprite .undoIcon { + gwt-image: 'undoNormal'; + border: none; +} + +.deleted { + background-color: deletedBackground; + color: #ffffff; + white-space: nowrap; + padding-left: 50px; +} + +.deleted:hover { + background-color: selectionColor; + color: textColor; + } + +.deletedBorder { + background: 1px solid deletedBackground; +} + +.deleteSectionHover { + background-color: selectionColor !important; +}
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteHover.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteHover.png new file mode 100644 index 0000000..839e8ef --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteHover.png Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteNormal.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteNormal.png new file mode 100644 index 0000000..ffddb6f --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/deleteNormal.png Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/editText.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/editText.png new file mode 100644 index 0000000..188e1c1 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/editText.png Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/undoNormal.png b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/undoNormal.png new file mode 100644 index 0000000..8b0fef9 --- /dev/null +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/undoNormal.png Binary files differ
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java index 364d710..4caf8f8 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PatchSetComplexDisclosurePanel.java
@@ -27,7 +27,8 @@ import com.google.gerrit.reviewdb.Account; import com.google.gerrit.reviewdb.AccountDiffPreference; import com.google.gerrit.reviewdb.AccountGeneralPreferences; -import com.google.gerrit.reviewdb.ApprovalCategory; +import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadCommand; +import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadScheme; import com.google.gerrit.reviewdb.Change; import com.google.gerrit.reviewdb.ChangeMessage; import com.google.gerrit.reviewdb.Patch; @@ -35,8 +36,6 @@ import com.google.gerrit.reviewdb.PatchSetInfo; import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.UserIdentity; -import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadCommand; -import com.google.gerrit.reviewdb.AccountGeneralPreferences.DownloadScheme; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; @@ -54,7 +53,6 @@ import com.google.gwt.user.client.ui.HTMLTable.CellFormatter; import com.google.gwtexpui.clippy.client.CopyableLabel; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -402,12 +400,8 @@ private void populateActions(final PatchSetDetail detail) { final boolean isOpen = changeDetail.getChange().getStatus().isOpen(); - Set<ApprovalCategory.Id> allowed = changeDetail.getCurrentActions(); - if (allowed == null) { - allowed = Collections.emptySet(); - } - if (isOpen && allowed.contains(ApprovalCategory.SUBMIT)) { + if (isOpen && changeDetail.canSubmit()) { final Button b = new Button(Util.M .submitPatchSet(detail.getPatchSet().getPatchSetId()));
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java index 0ed58b4..f7f8ae1 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/changes/PublishCommentScreen.java
@@ -25,8 +25,11 @@ import com.google.gerrit.client.ui.SmallHeading; import com.google.gerrit.common.PageLinks; import com.google.gerrit.common.data.ApprovalType; +import com.google.gerrit.common.data.ApprovalTypes; import com.google.gerrit.common.data.ChangeDetail; import com.google.gerrit.common.data.PatchSetPublishDetail; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.common.data.PermissionRange; import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.ApprovalCategoryValue; import com.google.gerrit.reviewdb.Change; @@ -39,11 +42,11 @@ import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.FormPanel; +import com.google.gwt.user.client.ui.FormPanel.SubmitEvent; import com.google.gwt.user.client.ui.Panel; import com.google.gwt.user.client.ui.RadioButton; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; -import com.google.gwt.user.client.ui.FormPanel.SubmitEvent; import com.google.gwtexpui.globalkey.client.NpTextArea; import com.google.gwtjsonrpc.client.VoidResult; @@ -54,7 +57,6 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; public class PublishCommentScreen extends AccountScreen implements ClickHandler, CommentEditorContainer { @@ -218,16 +220,25 @@ } private void initApprovals(final PatchSetPublishDetail r, final Panel body) { - for (final ApprovalType ct : Gerrit.getConfig().getApprovalTypes() - .getApprovalTypes()) { - if (r.isAllowed(ct.getCategory().getId())) { - initApprovalType(r, body, ct); + ApprovalTypes types = Gerrit.getConfig().getApprovalTypes(); + + for (ApprovalType type : types.getApprovalTypes()) { + String permission = Permission.forLabel(type.getCategory().getLabelName()); + PermissionRange range = r.getRange(permission); + if (range != null && !range.isEmpty()) { + initApprovalType(r, body, type, range); + } + } + + for (PermissionRange range : r.getLabels()) { + if (!range.isEmpty() && types.byLabel(range.getLabel()) == null) { + // TODO: this is a non-standard label. Offer it without the type. } } } private void initApprovalType(final PatchSetPublishDetail r, - final Panel body, final ApprovalType ct) { + final Panel body, final ApprovalType ct, final PermissionRange range) { body.add(new SmallHeading(ct.getCategory().getName() + ":")); final VerticalPanel vp = new VerticalPanel(); @@ -236,11 +247,10 @@ new ArrayList<ApprovalCategoryValue>(ct.getValues()); Collections.reverse(lst); final ApprovalCategory.Id catId = ct.getCategory().getId(); - final Set<ApprovalCategoryValue.Id> allowed = r.getAllowed(catId); final PatchSetApproval prior = r.getChangeApproval(catId); for (final ApprovalCategoryValue buttonValue : lst) { - if (!allowed.contains(buttonValue.getId())) { + if (!range.contains(buttonValue.getValue())) { continue; } @@ -306,7 +316,7 @@ } } - submit.setVisible(r.isSubmitAllowed()); + submit.setVisible(r.canSubmit()); } private void onSend(final boolean submit) {
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java index 0a9cedf..5d8ee8b 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/AccountGroupSuggestOracle.java
@@ -16,26 +16,34 @@ import com.google.gerrit.client.RpcStatus; import com.google.gerrit.client.rpc.GerritCallback; -import com.google.gerrit.reviewdb.AccountGroupName; +import com.google.gerrit.common.data.GroupReference; +import com.google.gerrit.reviewdb.AccountGroup; import com.google.gwt.user.client.ui.SuggestOracle; import com.google.gwtexpui.safehtml.client.HighlightSuggestOracle; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** Suggestion Oracle for AccountGroup entities. */ public class AccountGroupSuggestOracle extends HighlightSuggestOracle { + private Map<String, AccountGroup.UUID> priorResults = + new HashMap<String, AccountGroup.UUID>(); + @Override public void onRequestSuggestions(final Request req, final Callback callback) { RpcStatus.hide(new Runnable() { public void run() { SuggestUtil.SVC.suggestAccountGroup(req.getQuery(), req.getLimit(), - new GerritCallback<List<AccountGroupName>>() { - public void onSuccess(final List<AccountGroupName> result) { + new GerritCallback<List<GroupReference>>() { + public void onSuccess(final List<GroupReference> result) { + priorResults.clear(); final ArrayList<AccountGroupSuggestion> r = new ArrayList<AccountGroupSuggestion>(result.size()); - for (final AccountGroupName p : result) { + for (final GroupReference p : result) { r.add(new AccountGroupSuggestion(p)); + priorResults.put(p.getName(), p.getUUID()); } callback.onSuggestionsReady(req, new Response(r)); } @@ -46,9 +54,9 @@ private static class AccountGroupSuggestion implements SuggestOracle.Suggestion { - private final AccountGroupName info; + private final GroupReference info; - AccountGroupSuggestion(final AccountGroupName k) { + AccountGroupSuggestion(final GroupReference k) { info = k; } @@ -60,4 +68,9 @@ return info.getName(); } } + + /** @return the group UUID, or null if it cannot be found. */ + public AccountGroup.UUID getUUID(String name) { + return priorResults.get(name); + } }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Hyperlink.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Hyperlink.java index 6c18db1..1261b42 100644 --- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Hyperlink.java +++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/ui/Hyperlink.java
@@ -25,6 +25,10 @@ public class Hyperlink extends com.google.gwt.user.client.ui.Hyperlink { static final HyperlinkImpl impl = GWT.create(HyperlinkImpl.class); + /** Initialize a default hyperlink with no target and no text. */ + public Hyperlink() { + } + /** * Creates a hyperlink with its text and target history token specified. *
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java index 379fee6..adc48f8 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/SuggestServiceImpl.java
@@ -15,12 +15,12 @@ package com.google.gerrit.httpd.rpc; import com.google.gerrit.common.data.AccountInfo; +import com.google.gerrit.common.data.GroupReference; import com.google.gerrit.common.data.SuggestService; import com.google.gerrit.common.errors.NoSuchGroupException; import com.google.gerrit.reviewdb.Account; import com.google.gerrit.reviewdb.AccountExternalId; import com.google.gerrit.reviewdb.AccountGroup; -import com.google.gerrit.reviewdb.AccountGroup.Id; import com.google.gerrit.reviewdb.AccountGroupName; import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.ReviewDb; @@ -30,8 +30,10 @@ import com.google.gerrit.server.account.GroupControl; import com.google.gerrit.server.config.AuthConfig; import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.account.GroupCache; +import com.google.gerrit.server.project.NoSuchProjectException; import com.google.gerrit.server.project.ProjectCache; -import com.google.gerrit.server.project.ProjectState; +import com.google.gerrit.server.project.ProjectControl; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwtorm.client.OrmException; import com.google.inject.Inject; @@ -52,23 +54,28 @@ private static final String MAX_SUFFIX = "\u9fa5"; private final AuthConfig authConfig; + private final ProjectControl.Factory projectControlFactory; private final ProjectCache projectCache; private final AccountCache accountCache; private final GroupControl.Factory groupControlFactory; private final IdentifiedUser.GenericFactory userFactory; private final Provider<CurrentUser> currentUser; private final SuggestAccountsEnum suggestAccounts; + private final GroupCache groupCache; @Inject SuggestServiceImpl(final Provider<ReviewDb> schema, final AuthConfig authConfig, + final ProjectControl.Factory projectControlFactory, final ProjectCache projectCache, final AccountCache accountCache, final GroupControl.Factory groupControlFactory, final IdentifiedUser.GenericFactory userFactory, final Provider<CurrentUser> currentUser, - @GerritServerConfig final Config cfg) { + @GerritServerConfig final Config cfg, + final GroupCache groupCache) { super(schema, currentUser); this.authConfig = authConfig; + this.projectControlFactory = projectControlFactory; this.projectCache = projectCache; this.accountCache = accountCache; this.groupControlFactory = groupControlFactory; @@ -76,28 +83,29 @@ this.currentUser = currentUser; this.suggestAccounts = cfg.getEnum("suggest", null, "accounts", SuggestAccountsEnum.ALL); + this.groupCache = groupCache; } public void suggestProjectNameKey(final String query, final int limit, final AsyncCallback<List<Project.NameKey>> callback) { - run(callback, new Action<List<Project.NameKey>>() { - public List<Project.NameKey> run(final ReviewDb db) throws OrmException { - final String a = query; - final String b = a + MAX_SUFFIX; - final int max = 10; - final int n = limit <= 0 ? max : Math.min(limit, max); + final int max = 10; + final int n = limit <= 0 ? max : Math.min(limit, max); - final CurrentUser user = currentUser.get(); - final List<Project.NameKey> r = new ArrayList<Project.NameKey>(); - for (final Project p : db.projects().suggestByName(a, b, n)) { - final ProjectState e = projectCache.get(p.getNameKey()); - if (e != null && e.controlFor(user).isVisible()) { - r.add(p.getNameKey()); - } - } - return r; + final List<Project.NameKey> r = new ArrayList<Project.NameKey>(n); + for (final Project.NameKey nameKey : projectCache.byName(query)) { + final ProjectControl ctl; + try { + ctl = projectControlFactory.validateFor(nameKey); + } catch (NoSuchProjectException e) { + continue; } - }); + + r.add(ctl.getProject().getNameKey()); + if (r.size() == n) { + break; + } + } + callback.onSuccess(r); } public void suggestAccount(final String query, final Boolean active, @@ -154,10 +162,11 @@ map.put(account.getId(), info); break; case SAME_GROUP: { - Set<AccountGroup.Id> usersGroups = groupsOf(account); - usersGroups.removeAll(authConfig.getRegisteredGroups()); + Set<AccountGroup.UUID> usersGroups = groupsOf(account); + usersGroups.remove(AccountGroup.ANONYMOUS_USERS); + usersGroups.remove(AccountGroup.REGISTERED_USERS); usersGroups.remove(authConfig.getBatchUsersGroup()); - for (AccountGroup.Id myGroup : currentUser.get().getEffectiveGroups()) { + for (AccountGroup.UUID myGroup : currentUser.get().getEffectiveGroups()) { if (usersGroups.contains(myGroup)) { map.put(account.getId(), info); break; @@ -166,10 +175,11 @@ break; } case VISIBLE_GROUP: { - Set<AccountGroup.Id> usersGroups = groupsOf(account); - usersGroups.removeAll(authConfig.getRegisteredGroups()); + Set<AccountGroup.UUID> usersGroups = groupsOf(account); + usersGroups.remove(AccountGroup.ANONYMOUS_USERS); + usersGroups.remove(AccountGroup.REGISTERED_USERS); usersGroups.remove(authConfig.getBatchUsersGroup()); - for (AccountGroup.Id usersGroup : usersGroups) { + for (AccountGroup.UUID usersGroup : usersGroups) { try { if (groupControlFactory.controlFor(usersGroup).isVisible()) { map.put(account.getId(), info); @@ -188,33 +198,36 @@ } } - private Set<Id> groupsOf(Account account) { + private Set<AccountGroup.UUID> groupsOf(Account account) { IdentifiedUser user = userFactory.create(account.getId()); - return new HashSet<AccountGroup.Id>(user.getEffectiveGroups()); + return new HashSet<AccountGroup.UUID>(user.getEffectiveGroups()); } public void suggestAccountGroup(final String query, final int limit, - final AsyncCallback<List<AccountGroupName>> callback) { - run(callback, new Action<List<AccountGroupName>>() { - public List<AccountGroupName> run(final ReviewDb db) throws OrmException { + final AsyncCallback<List<GroupReference>> callback) { + run(callback, new Action<List<GroupReference>>() { + public List<GroupReference> run(final ReviewDb db) throws OrmException { final String a = query; final String b = a + MAX_SUFFIX; final int max = 10; final int n = limit <= 0 ? max : Math.min(limit, max); - Set<AccountGroup.Id> memberOf = currentUser.get().getEffectiveGroups(); - List<AccountGroupName> names = new ArrayList<AccountGroupName>(n); + Set<AccountGroup.UUID> memberOf = currentUser.get().getEffectiveGroups(); + List<GroupReference> r = new ArrayList<GroupReference>(n); for (AccountGroupName group : db.accountGroupNames() .suggestByName(a, b, n)) { try { if (memberOf.contains(group.getId()) || groupControlFactory.controlFor(group.getId()).isVisible()) { - names.add(group); + AccountGroup g = groupCache.get(group.getId()); + if (g != null && g.getGroupUUID() != null) { + r.add(GroupReference.forGroup(g)); + } } } catch (NoSuchGroupException e) { continue; } } - return names; + return r; } }); }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java index f638d48..4f62330 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/AgreementInfoFactory.java
@@ -22,6 +22,7 @@ import com.google.gerrit.reviewdb.ContributorAgreement; import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.server.IdentifiedUser; +import com.google.gerrit.server.account.GroupCache; import com.google.inject.Inject; import java.util.ArrayList; @@ -36,13 +37,16 @@ } private final ReviewDb db; + private final GroupCache groupCache; private final IdentifiedUser user; private AgreementInfo info; @Inject - AgreementInfoFactory(final ReviewDb db, final IdentifiedUser user) { + AgreementInfoFactory(final ReviewDb db, final GroupCache groupCache, + final IdentifiedUser user) { this.db = db; + this.groupCache = groupCache; this.user = user; } @@ -55,9 +59,14 @@ final List<AccountGroupAgreement> groupAccepted = new ArrayList<AccountGroupAgreement>(); - for (final AccountGroup.Id groupId : user.getEffectiveGroups()) { + for (final AccountGroup.UUID groupUUID : user.getEffectiveGroups()) { + AccountGroup group = groupCache.get(groupUUID); + if (group == null) { + continue; + } + final List<AccountGroupAgreement> temp = - db.accountGroupAgreements().byGroup(groupId).toList(); + db.accountGroupAgreements().byGroup(group.getId()).toList(); Collections.reverse(temp);
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java index a564907..578e866 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/GroupAdminServiceImpl.java
@@ -46,6 +46,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; +import java.util.HashSet; import java.util.List; import java.util.Set; @@ -118,8 +119,14 @@ createGroupFactory.create(newName).to(callback); } - public void groupDetail(final AccountGroup.Id groupId, - final AsyncCallback<GroupDetail> callback) { + public void groupDetail(AccountGroup.Id groupId, AccountGroup.UUID groupUUID, + AsyncCallback<GroupDetail> callback) { + if (groupId == null && groupUUID != null) { + AccountGroup g = groupCache.get(groupUUID); + if (g != null) { + groupId = g.getId(); + } + } groupDetailFactory.create(groupId).to(callback); } @@ -281,7 +288,7 @@ Collections.singleton(new AccountGroupIncludeAudit(m, getAccountId()))); db.accountGroupIncludes().insert(Collections.singleton(m)); - groupIncludeCache.evictInclude(a.getId()); + groupIncludeCache.evictInclude(a.getGroupUUID()); } return groupDetailFactory.create(groupId).call(); @@ -361,6 +368,7 @@ } final Account.Id me = getAccountId(); + final Set<AccountGroup.Id> groupsToEvict = new HashSet<AccountGroup.Id>(); for (final AccountGroupInclude.Key k : keys) { final AccountGroupInclude m = db.accountGroupIncludes().get(k); @@ -385,9 +393,12 @@ Collections.singleton(audit)); } db.accountGroupIncludes().delete(Collections.singleton(m)); - groupIncludeCache.evictInclude(m.getIncludeId()); + groupsToEvict.add(k.getIncludeId()); } } + for (AccountGroup group : db.accountGroups().get(groupsToEvict)) { + groupIncludeCache.evictInclude(group.getGroupUUID()); + } return VoidResult.INSTANCE; } });
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/MyGroupsFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/MyGroupsFactory.java index b3e993e..088fae2 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/MyGroupsFactory.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/MyGroupsFactory.java
@@ -42,10 +42,10 @@ @Override public List<AccountGroup> call() throws Exception { - final Set<AccountGroup.Id> effective = user.getEffectiveGroups(); + final Set<AccountGroup.UUID> effective = user.getEffectiveGroups(); final int cnt = effective.size(); final List<AccountGroup> groupList = new ArrayList<AccountGroup>(cnt); - for (final AccountGroup.Id groupId : effective) { + for (final AccountGroup.UUID groupId : effective) { groupList.add(groupCache.get(groupId)); } Collections.sort(groupList, new Comparator<AccountGroup>() {
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/RenameGroup.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/RenameGroup.java index d62f0c0..c8bf519 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/RenameGroup.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/account/RenameGroup.java
@@ -21,14 +21,19 @@ import com.google.gerrit.reviewdb.AccountGroup; import com.google.gerrit.reviewdb.AccountGroupName; import com.google.gerrit.reviewdb.ReviewDb; +import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.account.GroupControl; +import com.google.gerrit.server.git.RenameGroupOp; import com.google.gwtorm.client.OrmDuplicateKeyException; import com.google.gwtorm.client.OrmException; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; import java.util.Collections; +import java.util.Date; +import java.util.TimeZone; +import java.util.concurrent.TimeUnit; class RenameGroup extends Handler<GroupDetail> { interface Factory { @@ -39,6 +44,8 @@ private final GroupCache groupCache; private final GroupControl.Factory groupControlFactory; private final GroupDetailFactory.Factory groupDetailFactory; + private final RenameGroupOp.Factory renameGroupOpFactory; + private final IdentifiedUser currentUser; private final AccountGroup.Id groupId; private final String newName; @@ -47,11 +54,15 @@ RenameGroup(final ReviewDb db, final GroupCache groupCache, final GroupControl.Factory groupControlFactory, final GroupDetailFactory.Factory groupDetailFactory, + final RenameGroupOp.Factory renameGroupOpFactory, + final IdentifiedUser currentUser, @Assisted final AccountGroup.Id groupId, @Assisted final String newName) { this.db = db; this.groupCache = groupCache; this.groupControlFactory = groupControlFactory; this.groupDetailFactory = groupDetailFactory; + this.renameGroupOpFactory = renameGroupOpFactory; + this.currentUser = currentUser; this.groupId = groupId; this.newName = newName; } @@ -94,6 +105,10 @@ groupCache.evict(group); groupCache.evictAfterRename(old); + renameGroupOpFactory.create( // + currentUser.newCommitterIdent(new Date(), TimeZone.getDefault()), // + group.getGroupUUID(), // + old.get(), newName).start(0, TimeUnit.MILLISECONDS); return groupDetailFactory.create(groupId).call(); }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java index 560b155..d2d4ce0 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/ChangeDetailFactory.java
@@ -33,6 +33,7 @@ import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.AccountInfoCacheFactory; import com.google.gerrit.server.patch.PatchSetInfoNotAvailableException; +import com.google.gerrit.server.project.CanSubmitResult; import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.NoSuchChangeException; import com.google.gerrit.server.workflow.CategoryFunction; @@ -94,6 +95,7 @@ if (patch == null) { throw new NoSuchEntityException(); } + final CanSubmitResult canSubmitResult = control.canSubmit(patch.getId()); aic.want(change.getOwner()); @@ -103,6 +105,7 @@ detail.setCanAbandon(change.getStatus().isOpen() && control.canAbandon()); detail.setCanRestore(change.getStatus() == Change.Status.ABANDONED && control.canRestore()); + detail.setCanSubmit(canSubmitResult == CanSubmitResult.OK); detail.setStarred(control.getCurrentUser().getStarredChanges().contains( changeId)); @@ -141,23 +144,13 @@ final Set<ApprovalCategory.Id> missingApprovals = new HashSet<ApprovalCategory.Id>(); - final Set<ApprovalCategory.Id> currentActions = - new HashSet<ApprovalCategory.Id>(); - for (final ApprovalType at : approvalTypes.getApprovalTypes()) { CategoryFunction.forCategory(at.getCategory()).run(at, fs); if (!fs.isValid(at)) { missingApprovals.add(at.getCategory().getId()); } } - for (final ApprovalType at : approvalTypes.getActionTypes()) { - if (CategoryFunction.forCategory(at.getCategory()).isValid( - control.getCurrentUser(), at, fs)) { - currentActions.add(at.getCategory().getId()); - } - } detail.setMissingApprovals(missingApprovals); - detail.setCurrentActions(currentActions); } final boolean canRemoveReviewers = detail.getChange().getStatus().isOpen() //
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetPublishDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetPublishDetailFactory.java index 779475e..58daa09 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetPublishDetailFactory.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/changedetail/PatchSetPublishDetailFactory.java
@@ -15,19 +15,14 @@ package com.google.gerrit.httpd.rpc.changedetail; import com.google.gerrit.common.data.AccountInfoCache; -import com.google.gerrit.common.data.ApprovalType; -import com.google.gerrit.common.data.ApprovalTypes; import com.google.gerrit.common.data.PatchSetPublishDetail; +import com.google.gerrit.common.data.PermissionRange; import com.google.gerrit.httpd.rpc.Handler; -import com.google.gerrit.reviewdb.AccountGroup; -import com.google.gerrit.reviewdb.ApprovalCategory; -import com.google.gerrit.reviewdb.ApprovalCategoryValue; import com.google.gerrit.reviewdb.Change; import com.google.gerrit.reviewdb.PatchLineComment; import com.google.gerrit.reviewdb.PatchSet; import com.google.gerrit.reviewdb.PatchSetApproval; import com.google.gerrit.reviewdb.PatchSetInfo; -import com.google.gerrit.reviewdb.RefRight; import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.AccountInfoCacheFactory; @@ -36,27 +31,20 @@ import com.google.gerrit.server.project.CanSubmitResult; import com.google.gerrit.server.project.ChangeControl; import com.google.gerrit.server.project.NoSuchChangeException; -import com.google.gerrit.server.project.ProjectCache; -import com.google.gerrit.server.project.ProjectState; -import com.google.gerrit.server.project.RefControl; import com.google.gwtorm.client.OrmException; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; -import java.util.HashMap; -import java.util.HashSet; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import java.util.Map; -import java.util.Set; final class PatchSetPublishDetailFactory extends Handler<PatchSetPublishDetail> { interface Factory { PatchSetPublishDetailFactory create(PatchSet.Id patchSetId); } - private final ProjectCache projectCache; private final PatchSetInfoFactory infoFactory; - private final ApprovalTypes approvalTypes; private final ReviewDb db; private final ChangeControl.Factory changeControlFactory; private final AccountInfoCacheFactory aic; @@ -68,19 +56,14 @@ private PatchSetInfo patchSetInfo; private Change change; private List<PatchLineComment> drafts; - private Map<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>> allowed; - private Map<ApprovalCategory.Id, PatchSetApproval> given; @Inject PatchSetPublishDetailFactory(final PatchSetInfoFactory infoFactory, - final ProjectCache projectCache, final ApprovalTypes approvalTypes, final ReviewDb db, final AccountInfoCacheFactory.Factory accountInfoCacheFactory, final ChangeControl.Factory changeControlFactory, final IdentifiedUser user, @Assisted final PatchSet.Id patchSetId) { - this.projectCache = projectCache; this.infoFactory = infoFactory; - this.approvalTypes = approvalTypes; this.db = db; this.changeControlFactory = changeControlFactory; this.aic = accountInfoCacheFactory.create(); @@ -98,15 +81,17 @@ patchSetInfo = infoFactory.get(patchSetId); drafts = db.patchComments().draft(patchSetId, user.getAccountId()).toList(); - allowed = new HashMap<ApprovalCategory.Id, Set<ApprovalCategoryValue.Id>>(); - given = new HashMap<ApprovalCategory.Id, PatchSetApproval>(); + List<PermissionRange> allowed = Collections.emptyList(); + List<PatchSetApproval> given = Collections.emptyList(); + if (change.getStatus().isOpen() && patchSetId.equals(change.currentPatchSetId())) { - computeAllowed(); - for (final PatchSetApproval a : db.patchSetApprovals().byPatchSetUser( - patchSetId, user.getAccountId())) { - given.put(a.getCategoryId(), a); - } + allowed = new ArrayList<PermissionRange>(control.getLabelRanges()); + Collections.sort(allowed); + + given = db.patchSetApprovals() // + .byPatchSetUser(patchSetId, user.getAccountId()) // + .toList(); } aic.want(change.getOwner()); @@ -117,46 +102,12 @@ detail.setPatchSetInfo(patchSetInfo); detail.setChange(change); detail.setDrafts(drafts); - detail.setAllowed(allowed); + detail.setLabels(allowed); detail.setGiven(given); final CanSubmitResult canSubmitResult = control.canSubmit(patchSetId); - detail.setSubmitAllowed(canSubmitResult == CanSubmitResult.OK); + detail.setCanSubmit(canSubmitResult == CanSubmitResult.OK); return detail; } - - private void computeAllowed() { - final Set<AccountGroup.Id> am = user.getEffectiveGroups(); - final ProjectState pe = projectCache.get(change.getProject()); - for (ApprovalCategory.Id category : approvalTypes.getApprovalCategories()) { - RefControl rc = pe.controlFor(user).controlForRef(change.getDest()); - List<RefRight> categoryRights = rc.getApplicableRights(category); - computeAllowed(am, categoryRights, category); - } - } - - private void computeAllowed(final Set<AccountGroup.Id> am, - final List<RefRight> list, ApprovalCategory.Id category) { - - Set<ApprovalCategoryValue.Id> s = allowed.get(category); - if (s == null) { - s = new HashSet<ApprovalCategoryValue.Id>(); - allowed.put(category, s); - } - - for (final RefRight r : list) { - if (!am.contains(r.getAccountGroupId())) { - continue; - } - final ApprovalType at = - approvalTypes.getApprovalType(r.getApprovalCategoryId()); - for (short m = r.getMinValue(); m <= r.getMaxValue(); m++) { - final ApprovalCategoryValue v = at.getValue(m); - if (v != null) { - s.add(v.getId()); - } - } - } - } }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java index da52596..c81c2e9 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/patch/PatchDetailServiceImpl.java
@@ -187,11 +187,13 @@ for (final PatchSetApproval ca : db.patchSetApprovals() .byPatchSetUser(ps_id, aid)) { final ApprovalCategory.Id category = ca.getCategoryId(); - if (change.getStatus().isOpen()) { - fs.normalize(approvalTypes.getApprovalType(category), ca); + if (ApprovalCategory.SUBMIT.equals(category)) { + continue; } - if (ca.getValue() == 0 - || ApprovalCategory.SUBMIT.equals(category)) { + if (change.getStatus().isOpen()) { + fs.normalize(approvalTypes.byId(category), ca); + } + if (ca.getValue() == 0) { continue; } psas.put(category, ca); @@ -231,11 +233,13 @@ for (PatchSetApproval ca : db.patchSetApprovals().byPatchSet(ps_id)) { final ApprovalCategory.Id category = ca.getCategoryId(); - if (change.getStatus().isOpen()) { - fs.normalize(approvalTypes.getApprovalType(category), ca); + if (ApprovalCategory.SUBMIT.equals(category)) { + continue; } - if (ca.getValue() == 0 - || ApprovalCategory.SUBMIT.equals(category)) { + if (change.getStatus().isOpen()) { + fs.normalize(approvalTypes.byId(category), ca); + } + if (ca.getValue() == 0) { continue; } boolean keep = true;
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddRefRight.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddRefRight.java deleted file mode 100644 index 358b542..0000000 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/AddRefRight.java +++ /dev/null
@@ -1,207 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.httpd.rpc.project; - -import com.google.gerrit.common.data.ApprovalType; -import com.google.gerrit.common.data.ApprovalTypes; -import com.google.gerrit.common.data.ProjectDetail; -import com.google.gerrit.common.errors.InvalidNameException; -import com.google.gerrit.common.errors.NoSuchGroupException; -import com.google.gerrit.httpd.rpc.Handler; -import com.google.gerrit.reviewdb.AccountGroup; -import com.google.gerrit.reviewdb.ApprovalCategory; -import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.RefRight; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gerrit.server.account.GroupCache; -import com.google.gerrit.server.project.NoSuchProjectException; -import com.google.gerrit.server.project.NoSuchRefException; -import com.google.gerrit.server.project.ProjectCache; -import com.google.gerrit.server.project.ProjectControl; -import com.google.gerrit.server.project.RefControl; -import com.google.gwtorm.client.OrmException; -import com.google.inject.Inject; -import com.google.inject.assistedinject.Assisted; - -import org.eclipse.jgit.lib.Constants; -import org.eclipse.jgit.lib.Repository; - -import java.util.Collections; - -import javax.annotation.Nullable; - -class AddRefRight extends Handler<ProjectDetail> { - interface Factory { - AddRefRight create(@Assisted Project.NameKey projectName, - @Assisted ApprovalCategory.Id categoryId, - @Assisted("groupName") String groupName, - @Nullable @Assisted("refPattern") String refPattern, - @Assisted("min") short min, @Assisted("max") short max); - } - - private final ProjectDetailFactory.Factory projectDetailFactory; - private final ProjectControl.Factory projectControlFactory; - private final ProjectCache projectCache; - private final GroupCache groupCache; - private final ReviewDb db; - private final ApprovalTypes approvalTypes; - - private final Project.NameKey projectName; - private final ApprovalCategory.Id categoryId; - private final AccountGroup.NameKey groupName; - private final String refPattern; - private final short min; - private final short max; - - @Inject - AddRefRight(final ProjectDetailFactory.Factory projectDetailFactory, - final ProjectControl.Factory projectControlFactory, - final ProjectCache projectCache, final GroupCache groupCache, - final ReviewDb db, final ApprovalTypes approvalTypes, - - @Assisted final Project.NameKey projectName, - @Assisted final ApprovalCategory.Id categoryId, - @Assisted("groupName") final String groupName, - @Nullable @Assisted("refPattern") final String refPattern, - @Assisted("min") final short min, @Assisted("max") final short max) { - this.projectDetailFactory = projectDetailFactory; - this.projectControlFactory = projectControlFactory; - this.projectCache = projectCache; - this.groupCache = groupCache; - this.approvalTypes = approvalTypes; - this.db = db; - - this.projectName = projectName; - this.categoryId = categoryId; - this.groupName = new AccountGroup.NameKey(groupName); - this.refPattern = refPattern != null ? refPattern.trim() : null; - - if (min <= max) { - this.min = min; - this.max = max; - } else { - this.min = max; - this.max = min; - } - } - - @Override - public ProjectDetail call() throws NoSuchProjectException, OrmException, - NoSuchGroupException, InvalidNameException, NoSuchRefException { - final ProjectControl projectControl = - projectControlFactory.controlFor(projectName); - - final ApprovalType at = approvalTypes.getApprovalType(categoryId); - if (at == null || at.getValue(min) == null || at.getValue(max) == null) { - throw new IllegalArgumentException("Invalid category " + categoryId - + " or range " + min + ".." + max); - } - - String refPattern = this.refPattern; - if (refPattern == null || refPattern.isEmpty()) { - if (categoryId.equals(ApprovalCategory.SUBMIT) - || categoryId.equals(ApprovalCategory.PUSH_HEAD)) { - // Explicitly related to a branch head. - refPattern = Constants.R_HEADS + "*"; - - } else if (!at.getCategory().isAction()) { - // Non actions are approval votes on a change, assume these apply - // to branch heads only. - refPattern = Constants.R_HEADS + "*"; - - } else if (categoryId.equals(ApprovalCategory.PUSH_TAG)) { - // Explicitly related to the tag namespace. - refPattern = Constants.R_TAGS + "*"; - - } else if (categoryId.equals(ApprovalCategory.READ) - || categoryId.equals(ApprovalCategory.OWN)) { - // Currently these are project-wide rights, so apply that way. - refPattern = RefRight.ALL; - - } else { - // Assume project wide for the default. - refPattern = RefRight.ALL; - } - } - - boolean exclusive = refPattern.startsWith("-"); - if (exclusive) { - refPattern = refPattern.substring(1); - } - - while (refPattern.startsWith("/")) { - refPattern = refPattern.substring(1); - } - - if (refPattern.startsWith(RefRight.REGEX_PREFIX)) { - String example = RefControl.shortestExample(refPattern); - - if (!example.startsWith(Constants.R_REFS)) { - refPattern = RefRight.REGEX_PREFIX + Constants.R_HEADS - + refPattern.substring(RefRight.REGEX_PREFIX.length()); - example = RefControl.shortestExample(refPattern); - } - - if (!Repository.isValidRefName(example)) { - throw new InvalidNameException(); - } - - } else { - if (!refPattern.startsWith(Constants.R_REFS)) { - refPattern = Constants.R_HEADS + refPattern; - } - - if (refPattern.endsWith("/*")) { - final String prefix = refPattern.substring(0, refPattern.length() - 2); - if (!"refs".equals(prefix) && !Repository.isValidRefName(prefix)) { - throw new InvalidNameException(); - } - } else { - if (!Repository.isValidRefName(refPattern)) { - throw new InvalidNameException(); - } - } - } - - if (!projectControl.controlForRef(refPattern).isOwner()) { - throw new NoSuchRefException(refPattern); - } - - if (exclusive) { - refPattern = "-" + refPattern; - } - - final AccountGroup group = groupCache.get(groupName); - if (group == null) { - throw new NoSuchGroupException(groupName); - } - final RefRight.Key key = - new RefRight.Key(projectName, new RefRight.RefPattern(refPattern), - categoryId, group.getId()); - RefRight rr = db.refRights().get(key); - if (rr == null) { - rr = new RefRight(key); - rr.setMinValue(min); - rr.setMaxValue(max); - db.refRights().insert(Collections.singleton(rr)); - } else { - rr.setMinValue(min); - rr.setMaxValue(max); - db.refRights().update(Collections.singleton(rr)); - } - projectCache.evictAll(); - return projectDetailFactory.create(projectName).call(); - } -}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java new file mode 100644 index 0000000..ffe44a3 --- /dev/null +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectAccess.java
@@ -0,0 +1,206 @@ +// Copyright (C) 2010 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.httpd.rpc.project; + +import com.google.gerrit.common.data.AccessSection; +import com.google.gerrit.common.data.GroupReference; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.common.data.PermissionRule; +import com.google.gerrit.common.data.ProjectAccess; +import com.google.gerrit.common.errors.InvalidNameException; +import com.google.gerrit.common.errors.NoSuchGroupException; +import com.google.gerrit.httpd.rpc.Handler; +import com.google.gerrit.reviewdb.AccountGroup; +import com.google.gerrit.reviewdb.Project; +import com.google.gerrit.server.account.GroupCache; +import com.google.gerrit.server.git.MetaDataUpdate; +import com.google.gerrit.server.git.ProjectConfig; +import com.google.gerrit.server.project.NoSuchProjectException; +import com.google.gerrit.server.project.ProjectCache; +import com.google.gerrit.server.project.ProjectControl; +import com.google.gerrit.server.project.RefControl; +import com.google.gwtorm.client.OrmConcurrencyException; +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; + +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.Repository; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.Nullable; + +class ChangeProjectAccess extends Handler<ProjectAccess> { + interface Factory { + ChangeProjectAccess create(@Assisted Project.NameKey projectName, + @Assisted ObjectId base, @Assisted List<AccessSection> sectionList, + @Nullable @Assisted String message); + } + + private final ProjectAccessFactory.Factory projectAccessFactory; + private final ProjectControl.Factory projectControlFactory; + private final ProjectCache projectCache; + private final GroupCache groupCache; + private final MetaDataUpdate.User metaDataUpdateFactory; + + private final Project.NameKey projectName; + private final ObjectId base; + private List<AccessSection> sectionList; + private String message; + + @Inject + ChangeProjectAccess(final ProjectAccessFactory.Factory projectAccessFactory, + final ProjectControl.Factory projectControlFactory, + final ProjectCache projectCache, final GroupCache groupCache, + final MetaDataUpdate.User metaDataUpdateFactory, + + @Assisted final Project.NameKey projectName, + @Assisted final ObjectId base, @Assisted List<AccessSection> sectionList, + @Nullable @Assisted String message) { + this.projectAccessFactory = projectAccessFactory; + this.projectControlFactory = projectControlFactory; + this.projectCache = projectCache; + this.groupCache = groupCache; + this.metaDataUpdateFactory = metaDataUpdateFactory; + + this.projectName = projectName; + this.base = base; + this.sectionList = sectionList; + this.message = message; + } + + @Override + public ProjectAccess call() throws NoSuchProjectException, IOException, + ConfigInvalidException, InvalidNameException, NoSuchGroupException, + OrmConcurrencyException { + final ProjectControl projectControl = + projectControlFactory.controlFor(projectName); + + final MetaDataUpdate md; + try { + md = metaDataUpdateFactory.create(projectName); + } catch (RepositoryNotFoundException notFound) { + throw new NoSuchProjectException(projectName); + } + try { + ProjectConfig config = ProjectConfig.read(md, base); + Set<String> toDelete = scanSectionNames(config); + + for (AccessSection section : mergeSections(sectionList)) { + final String name = section.getRefPattern(); + 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(); + } + + 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()) { + config.remove(config.getAccessSection(name)); + } + } + + if (message != null && !message.isEmpty()) { + if (!message.endsWith("\n")) { + message += "\n"; + } + md.setMessage(message); + } else { + md.setMessage("Modify access rules\n"); + } + + if (config.commit(md)) { + projectCache.evict(config.getProject()); + return projectAccessFactory.create(projectName).call(); + + } else { + throw new OrmConcurrencyException("Cannot update " + projectName); + } + } finally { + md.close(); + } + } + + private static List<AccessSection> mergeSections(List<AccessSection> src) { + Map<String, AccessSection> map = new LinkedHashMap<String, AccessSection>(); + for (AccessSection section : src) { + if (section.getPermissions().isEmpty()) { + continue; + } + + AccessSection prior = map.get(section.getRefPattern()); + if (prior != null) { + prior.mergeFrom(section); + } else { + map.put(section.getRefPattern(), section); + } + } + return new ArrayList<AccessSection>(map.values()); + } + + private static Set<String> scanSectionNames(ProjectConfig config) { + Set<String> names = new HashSet<String>(); + for (AccessSection section : config.getAccessSections()) { + names.add(section.getRefPattern()); + } + return names; + } + + private void lookupGroup(PermissionRule rule) throws NoSuchGroupException { + GroupReference ref = rule.getGroup(); + if (ref.getUUID() == null) { + AccountGroup.NameKey name = new AccountGroup.NameKey(ref.getName()); + AccountGroup group = groupCache.get(name); + if (group == null) { + throw new NoSuchGroupException(name); + } + ref.setUUID(group.getGroupUUID()); + } + } +}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectSettings.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectSettings.java index 88c85b8f..2b0f856 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectSettings.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ChangeProjectSettings.java
@@ -17,16 +17,21 @@ import com.google.gerrit.common.data.ProjectDetail; import com.google.gerrit.httpd.rpc.Handler; import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.git.MetaDataUpdate; +import com.google.gerrit.server.git.ProjectConfig; import com.google.gerrit.server.project.NoSuchProjectException; import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectControl; +import com.google.gwtorm.client.OrmConcurrencyException; import com.google.gwtorm.client.OrmException; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; -import java.util.Collections; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.errors.RepositoryNotFoundException; + +import java.io.IOException; class ChangeProjectSettings extends Handler<ProjectDetail> { interface Factory { @@ -36,8 +41,8 @@ private final ProjectDetailFactory.Factory projectDetailFactory; private final ProjectControl.Factory projectControlFactory; private final ProjectCache projectCache; - private final ReviewDb db; - private final GitRepositoryManager repoManager; + private final GitRepositoryManager mgr; + private final MetaDataUpdate.User metaDataUpdateFactory; private final Project update; @@ -45,14 +50,14 @@ ChangeProjectSettings( final ProjectDetailFactory.Factory projectDetailFactory, final ProjectControl.Factory projectControlFactory, - final ProjectCache projectCache, final ReviewDb db, - final GitRepositoryManager grm, + final ProjectCache projectCache, final GitRepositoryManager mgr, + final MetaDataUpdate.User metaDataUpdateFactory, @Assisted final Project update) { this.projectDetailFactory = projectDetailFactory; this.projectControlFactory = projectControlFactory; this.projectCache = projectCache; - this.db = db; - this.repoManager = grm; + this.mgr = mgr; + this.metaDataUpdateFactory = metaDataUpdateFactory; this.update = update; } @@ -60,20 +65,34 @@ @Override public ProjectDetail call() throws NoSuchProjectException, OrmException { final Project.NameKey projectName = update.getNameKey(); - final ProjectControl projectControl = - projectControlFactory.ownerFor(projectName); + projectControlFactory.ownerFor(projectName); - final Project proj = db.projects().get(projectName); - if (proj == null) { + final MetaDataUpdate md; + try { + md = metaDataUpdateFactory.create(projectName); + } catch (RepositoryNotFoundException notFound) { throw new NoSuchProjectException(projectName); } + try { + // TODO We really should take advantage of the Git commit DAG and + // ensure the current version matches the old version the caller read. + // + ProjectConfig config = ProjectConfig.read(md); + config.getProject().copySettingsFrom(update); - proj.copySettingsFrom(update); - db.projects().update(Collections.singleton(proj)); - projectCache.evict(proj); - - if (!projectControl.getProjectState().isSpecialWildProject()) { - repoManager.setProjectDescription(projectName, update.getDescription()); + md.setMessage("Modified project settings\n"); + if (config.commit(md)) { + mgr.setProjectDescription(projectName, update.getDescription()); + projectCache.evict(config.getProject()); + } else { + throw new OrmConcurrencyException("Cannot update " + projectName); + } + } catch (ConfigInvalidException err) { + throw new OrmException("Cannot read project " + projectName, err); + } catch (IOException err) { + throw new OrmException("Cannot update project " + projectName, err); + } finally { + md.close(); } return projectDetailFactory.create(projectName).call();
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteRefRights.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteRefRights.java deleted file mode 100644 index ae8b98b..0000000 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/DeleteRefRights.java +++ /dev/null
@@ -1,91 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.httpd.rpc.project; - -import com.google.gerrit.common.data.ProjectDetail; -import com.google.gerrit.httpd.rpc.Handler; -import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.RefRight; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gerrit.server.project.NoSuchProjectException; -import com.google.gerrit.server.project.NoSuchRefException; -import com.google.gerrit.server.project.ProjectCache; -import com.google.gerrit.server.project.ProjectControl; -import com.google.gwtorm.client.OrmException; -import com.google.inject.Inject; -import com.google.inject.assistedinject.Assisted; - -import java.util.Collections; -import java.util.Set; - -class DeleteRefRights extends Handler<ProjectDetail> { - interface Factory { - DeleteRefRights create(@Assisted Project.NameKey projectName, - @Assisted Set<RefRight.Key> toRemove); - } - - private final ProjectDetailFactory.Factory projectDetailFactory; - private final ProjectControl.Factory projectControlFactory; - private final ProjectCache projectCache; - private final ReviewDb db; - - private final Project.NameKey projectName; - private final Set<RefRight.Key> toRemove; - - @Inject - DeleteRefRights(final ProjectDetailFactory.Factory projectDetailFactory, - final ProjectControl.Factory projectControlFactory, - final ProjectCache projectCache, final ReviewDb db, - - @Assisted final Project.NameKey projectName, - @Assisted final Set<RefRight.Key> toRemove) { - this.projectDetailFactory = projectDetailFactory; - this.projectControlFactory = projectControlFactory; - this.projectCache = projectCache; - this.db = db; - - this.projectName = projectName; - this.toRemove = toRemove; - } - - @Override - public ProjectDetail call() throws NoSuchProjectException, OrmException, - NoSuchRefException { - final ProjectControl projectControl = - projectControlFactory.controlFor(projectName); - - for (final RefRight.Key k : toRemove) { - if (!projectName.equals(k.getProjectNameKey())) { - throw new IllegalArgumentException("All keys must be from same project"); - } - String refPattern = k.getRefPattern(); - if (refPattern.startsWith("-")) { - refPattern = refPattern.substring(1); - } - if (!projectControl.controlForRef(refPattern).isOwner()) { - throw new NoSuchRefException(refPattern); - } - } - - for (final RefRight.Key k : toRemove) { - final RefRight m = db.refRights().get(k); - if (m != null) { - db.refRights().delete(Collections.singleton(m)); - } - } - projectCache.evictAll(); - return projectDetailFactory.create(projectName).call(); - } -}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java new file mode 100644 index 0000000..3ae3ac5 --- /dev/null +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAccessFactory.java
@@ -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.httpd.rpc.project; + +import com.google.gerrit.common.data.AccessSection; +import com.google.gerrit.common.data.ProjectAccess; +import com.google.gerrit.httpd.rpc.Handler; +import com.google.gerrit.reviewdb.Project; +import com.google.gerrit.server.account.GroupCache; +import com.google.gerrit.server.config.WildProjectName; +import com.google.gerrit.server.git.MetaDataUpdate; +import com.google.gerrit.server.git.ProjectConfig; +import com.google.gerrit.server.project.NoSuchProjectException; +import com.google.gerrit.server.project.ProjectCache; +import com.google.gerrit.server.project.ProjectControl; +import com.google.gerrit.server.project.RefControl; +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; + +import org.eclipse.jgit.errors.ConfigInvalidException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +class ProjectAccessFactory extends Handler<ProjectAccess> { + interface Factory { + ProjectAccessFactory create(@Assisted Project.NameKey name); + } + + private final GroupCache groupCache; + private final ProjectCache projectCache; + private final ProjectControl.Factory projectControlFactory; + private final MetaDataUpdate.Server metaDataUpdateFactory; + private final Project.NameKey wildProject; + + private final Project.NameKey projectName; + private ProjectControl pc; + + @Inject + ProjectAccessFactory(final GroupCache groupCache, + final ProjectCache projectCache, + final ProjectControl.Factory projectControlFactory, + final MetaDataUpdate.Server metaDataUpdateFactory, + @WildProjectName final Project.NameKey wildProject, + + + @Assisted final Project.NameKey name) { + this.groupCache = groupCache; + this.projectCache = projectCache; + this.projectControlFactory = projectControlFactory; + this.metaDataUpdateFactory = metaDataUpdateFactory; + this.wildProject = wildProject; + + this.projectName = name; + } + + @Override + public ProjectAccess call() throws NoSuchProjectException, IOException, + ConfigInvalidException { + pc = open(); + + // Load the current configuration from the repository, ensuring its the most + // recent version available. If it differs from what was in the project + // state, force a cache flush now. + // + ProjectConfig config; + MetaDataUpdate md = metaDataUpdateFactory.create(projectName); + try { + config = ProjectConfig.read(md); + + if (config.updateGroupNames(groupCache)) { + md.setMessage("Update group names\n"); + if (config.commit(md)) { + projectCache.evict(config.getProject()); + pc = open(); + } + } else if (config.getRevision() != null + && !config.getRevision().equals( + pc.getProjectState().getConfig().getRevision())) { + projectCache.evict(config.getProject()); + pc = open(); + } + } finally { + md.close(); + } + + List<AccessSection> local = new ArrayList<AccessSection>(); + Set<String> ownerOf = new HashSet<String>(); + for (AccessSection section : config.getAccessSections()) { + RefControl rc = pc.controlForRef(section.getRefPattern()); + if (rc.isOwner()) { + local.add(section); + ownerOf.add(section.getRefPattern()); + } else if (rc.isVisible()) { + local.add(section); + } + } + + final ProjectAccess detail = new ProjectAccess(); + detail.setRevision(config.getRevision().name()); + detail.setLocal(local); + detail.setOwnerOf(ownerOf); + + if (projectName.equals(wildProject)) { + detail.setInheritsFrom(null); + } else if (config.getProject().getParent() != null) { + detail.setInheritsFrom(config.getProject().getParent()); + } else { + detail.setInheritsFrom(wildProject); + } + + return detail; + } + + private ProjectControl open() throws NoSuchProjectException { + return projectControlFactory.validateFor( // + projectName, // + ProjectControl.OWNER | ProjectControl.VISIBLE); + } +}
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java index 9b06dd9..0f9ffff 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectAdminServiceImpl.java
@@ -14,46 +14,48 @@ package com.google.gerrit.httpd.rpc.project; +import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.ListBranchesResult; +import com.google.gerrit.common.data.ProjectAccess; import com.google.gerrit.common.data.ProjectAdminService; import com.google.gerrit.common.data.ProjectDetail; -import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.Branch; import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.RefRight; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.inject.Inject; +import org.eclipse.jgit.lib.ObjectId; + import java.util.List; import java.util.Set; class ProjectAdminServiceImpl implements ProjectAdminService { private final AddBranch.Factory addBranchFactory; + private final ChangeProjectAccess.Factory changeProjectAccessFactory; private final ChangeProjectSettings.Factory changeProjectSettingsFactory; private final DeleteBranches.Factory deleteBranchesFactory; private final ListBranches.Factory listBranchesFactory; private final VisibleProjects.Factory visibleProjectsFactory; + private final ProjectAccessFactory.Factory projectAccessFactory; private final ProjectDetailFactory.Factory projectDetailFactory; - private final AddRefRight.Factory addRefRightFactory; - private final DeleteRefRights.Factory deleteRefRightsFactory; @Inject ProjectAdminServiceImpl(final AddBranch.Factory addBranchFactory, + final ChangeProjectAccess.Factory changeProjectAccessFactory, final ChangeProjectSettings.Factory changeProjectSettingsFactory, final DeleteBranches.Factory deleteBranchesFactory, final ListBranches.Factory listBranchesFactory, final VisibleProjects.Factory visibleProjectsFactory, - final ProjectDetailFactory.Factory projectDetailFactory, - final AddRefRight.Factory addRefRightFactory, - final DeleteRefRights.Factory deleteRefRightsFactory) { + final ProjectAccessFactory.Factory projectAccessFactory, + final ProjectDetailFactory.Factory projectDetailFactory) { this.addBranchFactory = addBranchFactory; + this.changeProjectAccessFactory = changeProjectAccessFactory; this.changeProjectSettingsFactory = changeProjectSettingsFactory; this.deleteBranchesFactory = deleteBranchesFactory; this.listBranchesFactory = listBranchesFactory; this.visibleProjectsFactory = visibleProjectsFactory; + this.projectAccessFactory = projectAccessFactory; this.projectDetailFactory = projectDetailFactory; - this.addRefRightFactory = addRefRightFactory; - this.deleteRefRightsFactory = deleteRefRightsFactory; } @Override @@ -68,24 +70,23 @@ } @Override + public void projectAccess(final Project.NameKey projectName, + final AsyncCallback<ProjectAccess> callback) { + projectAccessFactory.create(projectName).to(callback); + } + + @Override public void changeProjectSettings(final Project update, final AsyncCallback<ProjectDetail> callback) { changeProjectSettingsFactory.create(update).to(callback); } @Override - public void deleteRight(final Project.NameKey projectName, - final Set<RefRight.Key> toRemove, final AsyncCallback<ProjectDetail> callback) { - deleteRefRightsFactory.create(projectName, toRemove).to(callback); - } - - @Override - public void addRight(final Project.NameKey projectName, - final ApprovalCategory.Id categoryId, final String groupName, - final String refPattern, final short min, final short max, - final AsyncCallback<ProjectDetail> callback) { - addRefRightFactory.create(projectName, categoryId, groupName, refPattern, - min, max).to(callback); + public void changeProjectAccess(Project.NameKey projectName, + String baseRevision, String msg, List<AccessSection> sections, + AsyncCallback<ProjectAccess> cb) { + ObjectId base = ObjectId.fromString(baseRevision); + changeProjectAccessFactory.create(projectName, base, sections, msg).to(cb); } @Override
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectDetailFactory.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectDetailFactory.java index ef632c4..1eb940b 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectDetailFactory.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectDetailFactory.java
@@ -14,50 +14,28 @@ package com.google.gerrit.httpd.rpc.project; -import com.google.gerrit.common.data.ApprovalType; -import com.google.gerrit.common.data.ApprovalTypes; -import com.google.gerrit.common.data.InheritedRefRight; import com.google.gerrit.common.data.ProjectDetail; import com.google.gerrit.httpd.rpc.Handler; -import com.google.gerrit.reviewdb.AccountGroup; import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.RefRight; -import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.project.NoSuchProjectException; import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.server.project.ProjectState; -import com.google.gerrit.server.project.RefControl; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Set; - class ProjectDetailFactory extends Handler<ProjectDetail> { interface Factory { ProjectDetailFactory create(@Assisted Project.NameKey name); } - private final ApprovalTypes approvalTypes; - private final GroupCache groupCache; private final ProjectControl.Factory projectControlFactory; private final Project.NameKey projectName; - private Map<AccountGroup.Id, AccountGroup> groups; @Inject - ProjectDetailFactory(final ApprovalTypes approvalTypes, - final GroupCache groupCache, - final ProjectControl.Factory projectControlFactory, + ProjectDetailFactory(final ProjectControl.Factory projectControlFactory, @Assisted final Project.NameKey name) { - this.approvalTypes = approvalTypes; - this.groupCache = groupCache; this.projectControlFactory = projectControlFactory; this.projectName = name; @@ -72,88 +50,13 @@ final ProjectDetail detail = new ProjectDetail(); detail.setProject(projectState.getProject()); - groups = new HashMap<AccountGroup.Id, AccountGroup>(); - final List<InheritedRefRight> refRights = new ArrayList<InheritedRefRight>(); - - for (final RefRight r : projectState.getInheritedRights()) { - RefControl rc = pc.controlForRef(r.getRefPattern()); - boolean isOwner = rc.isOwner(); - - if (!isOwner && !rc.isVisible()) { - continue; - } - - InheritedRefRight refRight = new InheritedRefRight(r, true, isOwner); - if (!refRights.contains(refRight)) { - refRights.add(refRight); - wantGroup(r.getAccountGroupId()); - } - } - - for (final RefRight r : projectState.getLocalRights()) { - RefControl rc = pc.controlForRef(r.getRefPattern()); - boolean isOwner = rc.isOwner(); - - if (!isOwner && !rc.isVisible()) { - continue; - } - - refRights.add(new InheritedRefRight(r, false, isOwner)); - wantGroup(r.getAccountGroupId()); - } - - loadGroups(); - - Collections.sort(refRights, new Comparator<InheritedRefRight>() { - @Override - public int compare(final InheritedRefRight a, final InheritedRefRight b) { - final RefRight right1 = a.getRight(); - final RefRight right2 = b.getRight(); - int rc = categoryOf(right1).compareTo(categoryOf(right2)); - if (rc == 0) { - rc = right1.getRefPattern().compareTo(right2.getRefPattern()); - } - if (rc == 0) { - rc = groupOf(right1).compareTo(groupOf(right2)); - } - return rc; - } - - private String categoryOf(final RefRight r) { - final ApprovalType type = - approvalTypes.getApprovalType(r.getApprovalCategoryId()); - if (type == null) { - return r.getApprovalCategoryId().get(); - } - return type.getCategory().getName(); - } - - private String groupOf(final RefRight r) { - return groups.get(r.getAccountGroupId()).getName(); - } - }); - final boolean userIsOwner = pc.isOwner(); final boolean userIsOwnerAnyRef = pc.isOwnerAnyRef(); - detail.setRights(refRights); - detail.setGroups(groups); detail.setCanModifyAccess(userIsOwnerAnyRef); detail.setCanModifyAgreements(userIsOwner); detail.setCanModifyDescription(userIsOwner); detail.setCanModifyMergeType(userIsOwner); return detail; } - - private void wantGroup(final AccountGroup.Id id) { - groups.put(id, null); - } - - private void loadGroups() { - final Set<AccountGroup.Id> toGet = groups.keySet(); - groups = new HashMap<AccountGroup.Id, AccountGroup>(); - for (AccountGroup.Id groupId : toGet) { - groups.put(groupId, groupCache.get(groupId)); - } - } }
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java index 7932c79..68b3625 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/ProjectModule.java
@@ -29,12 +29,12 @@ @Override protected void configure() { factory(AddBranch.Factory.class); - factory(AddRefRight.Factory.class); + factory(ChangeProjectAccess.Factory.class); factory(ChangeProjectSettings.Factory.class); factory(DeleteBranches.Factory.class); - factory(DeleteRefRights.Factory.class); factory(ListBranches.Factory.class); factory(VisibleProjects.Factory.class); + factory(ProjectAccessFactory.Factory.class); factory(ProjectDetailFactory.Factory.class); } });
diff --git a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/VisibleProjects.java b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/VisibleProjects.java index 2588350..31ba77e 100644 --- a/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/VisibleProjects.java +++ b/gerrit-httpd/src/main/java/com/google/gerrit/httpd/rpc/project/VisibleProjects.java
@@ -17,11 +17,9 @@ import com.google.gerrit.httpd.rpc.Handler; import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.project.NoSuchProjectException; +import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectControl; -import com.google.gwtorm.client.OrmException; import com.google.inject.Inject; import java.util.ArrayList; @@ -35,33 +33,26 @@ } private final ProjectControl.Factory projectControlFactory; - private final CurrentUser user; - private final ReviewDb db; + private final ProjectCache projectCache; @Inject VisibleProjects(final ProjectControl.Factory projectControlFactory, - final CurrentUser user, final ReviewDb db) { + final ProjectCache projectCache) { this.projectControlFactory = projectControlFactory; - this.user = user; - this.db = db; + this.projectCache = projectCache; } @Override - public List<Project> call() throws OrmException { - final List<Project> result; - if (user.isAdministrator()) { - result = db.projects().all().toList(); - } else { - result = new ArrayList<Project>(); - for (Project p : db.projects().all().toList()) { - try { - ProjectControl c = projectControlFactory.controlFor(p.getNameKey()); - if (c.isVisible() || c.isOwner()) { - result.add(p); - } - } catch (NoSuchProjectException e) { - continue; + public List<Project> call() { + List<Project> result = new ArrayList<Project>(); + for (Project.NameKey p : projectCache.all()) { + try { + ProjectControl c = projectControlFactory.controlFor(p); + if (c.isVisible() || c.isOwner()) { + result.add(c.getProject()); } + } catch (NoSuchProjectException e) { + continue; } } Collections.sort(result, new Comparator<Project>() {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java index 7127027..68ac33d 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Init.java
@@ -31,9 +31,7 @@ import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.server.config.SitePath; import com.google.gerrit.server.config.SitePaths; -import com.google.gerrit.server.git.GitProjectImporter; import com.google.gerrit.server.git.GitRepositoryManager; -import com.google.gerrit.server.git.LocalDiskRepositoryManager; import com.google.gerrit.server.schema.SchemaUpdater; import com.google.gerrit.server.schema.UpdateUI; import com.google.gerrit.server.util.HostPlatform; @@ -62,9 +60,6 @@ @Option(name = "--batch", usage = "Batch mode; skip interactive prompting") private boolean batchMode; - @Option(name = "--import-projects", usage = "Import git repositories as projects") - private boolean importProjects; - @Option(name = "--no-auto-start", usage = "Don't automatically start daemon after init") private boolean noAutoStart; @@ -73,7 +68,6 @@ ErrorLogFile.errorOnlyConsole(); final SiteInit init = createSiteInit(); - init.flags.importProjects = importProjects; init.flags.autoStart = !noAutoStart && init.site.isNew; final SiteRun run; @@ -83,7 +77,6 @@ run = createSiteRun(init); run.upgradeSchema(); - run.importGit(); } catch (Exception failure) { if (init.flags.deleteOnFailure) { recursiveDelete(getSitePath()); @@ -166,7 +159,6 @@ final SchemaUpdater schemaUpdater; final SchemaFactory<ReviewDb> schema; final GitRepositoryManager repositoryManager; - final GitProjectImporter gitProjectImporter; final Browser browser; @Inject @@ -174,14 +166,13 @@ final SchemaUpdater schemaUpdater, final SchemaFactory<ReviewDb> schema, final GitRepositoryManager repositoryManager, - final GitProjectImporter gitProjectImporter, final Browser browser) { + final Browser browser) { this.ui = ui; this.site = site; this.flags = flags; this.schemaUpdater = schemaUpdater; this.schema = schema; this.repositoryManager = repositoryManager; - this.gitProjectImporter = gitProjectImporter; this.browser = browser; } @@ -241,23 +232,6 @@ } } - void importGit() throws OrmException, IOException { - if (flags.importProjects) { - gitProjectImporter.run(new GitProjectImporter.Messages() { - @Override - public void info(String msg) { - System.err.println(msg); - System.err.flush(); - } - - @Override - public void warning(String msg) { - info(msg); - } - }); - } - } - void start() throws Exception { if (flags.autoStart) { if (HostPlatform.isWin32()) { @@ -317,9 +291,6 @@ protected void configure() { bind(ConsoleUI.class).toInstance(init.ui); bind(InitFlags.class).toInstance(init.flags); - - bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class); - bind(GitProjectImporter.class); } }); return createDbInjector(SINGLE_USER).createChildInjector(modules);
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ScanTrackingIds.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ScanTrackingIds.java index beeed24..45f560a 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ScanTrackingIds.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/ScanTrackingIds.java
@@ -17,7 +17,6 @@ import static com.google.gerrit.server.schema.DataSourceProvider.Context.MULTI_USER; import com.google.gerrit.lifecycle.LifecycleManager; -import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.pgm.util.SiteProgram; import com.google.gerrit.reviewdb.Change; import com.google.gerrit.reviewdb.PatchSet; @@ -26,7 +25,6 @@ import com.google.gerrit.server.ChangeUtil; import com.google.gerrit.server.config.TrackingFooters; import com.google.gerrit.server.git.GitRepositoryManager; -import com.google.gerrit.server.git.LocalDiskRepositoryManager; import com.google.gwtorm.client.OrmException; import com.google.gwtorm.client.SchemaFactory; import com.google.inject.Inject; @@ -56,7 +54,6 @@ private List<Change> todo; private Injector dbInjector; - private Injector gitInjector; @Inject private TrackingFooters footers; @@ -74,17 +71,9 @@ } dbInjector = createDbInjector(MULTI_USER); - gitInjector = dbInjector.createChildInjector(new LifecycleModule() { - @Override - protected void configure() { - bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class); - listener().to(LocalDiskRepositoryManager.Lifecycle.class); - } - }); - - manager.add(dbInjector, gitInjector); + manager.add(dbInjector); manager.start(); - gitInjector.injectMembers(this); + dbInjector.injectMembers(this); final ReviewDb db = database.open(); try {
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitFlags.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitFlags.java index 992c616..5d71b48 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitFlags.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitFlags.java
@@ -30,9 +30,6 @@ /** Recursively delete the site path if initialization fails. */ public boolean deleteOnFailure; - /** Run the Git project importer after initialization. */ - public boolean importProjects; - /** Run the daemon (and open the web UI in a browser) after initialization. */ public boolean autoStart;
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitGitManager.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitGitManager.java index 2d95577..f0cd31f 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitGitManager.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/init/InitGitManager.java
@@ -25,14 +25,11 @@ /** Initialize the GitRepositoryManager configuration section. */ @Singleton class InitGitManager implements InitStep { - private final InitFlags flags; private final ConsoleUI ui; private final Section gerrit; @Inject - InitGitManager(final InitFlags flags, final ConsoleUI ui, - final Section.Factory sections) { - this.flags = flags; + InitGitManager(final ConsoleUI ui, final Section.Factory sections) { this.ui = ui; this.gerrit = sections.get("gerrit"); } @@ -44,11 +41,7 @@ if (d == null) { throw die("gerrit.basePath is required"); } - if (d.exists()) { - if (!flags.importProjects && d.list() != null && d.list().length > 0) { - flags.importProjects = ui.yesno(true, "Import existing repositories"); - } - } else if (!d.mkdirs()) { + if (!d.exists() && !d.mkdirs()) { throw die("Cannot create " + d); } }
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java index 8e3306b..340168c 100644 --- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java +++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java
@@ -22,6 +22,7 @@ import com.google.gerrit.server.config.SitePath; import com.google.gerrit.server.schema.DataSourceProvider; import com.google.gerrit.server.schema.DatabaseModule; +import com.google.gerrit.server.schema.SchemaModule; import com.google.gwtorm.client.OrmException; import com.google.inject.AbstractModule; import com.google.inject.CreationException; @@ -162,6 +163,7 @@ }); modules.add(new GerritServerConfigModule()); modules.add(new DatabaseModule()); + modules.add(new SchemaModule()); try { return Guice.createInjector(PRODUCTION, modules);
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroup.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroup.java index d2aceaa..5d8a4b9 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroup.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroup.java
@@ -46,6 +46,39 @@ } } + /** Globally unique identifier. */ + public static class UUID extends + StringKey<com.google.gwtorm.client.Key<?>> { + private static final long serialVersionUID = 1L; + + @Column(id = 1, length = 40) + protected String uuid; + + protected UUID() { + } + + public UUID(final String n) { + uuid = n; + } + + @Override + public String get() { + return uuid; + } + + @Override + protected void set(String newValue) { + uuid = newValue; + } + + /** Parse an AccountGroup.UUID out of a string representation. */ + public static UUID parse(final String str) { + final UUID r = new UUID(); + r.fromString(str); + return r; + } + } + /** Distinguished name, within organization directory server. */ public static class ExternalNameKey extends StringKey<com.google.gwtorm.client.Key<?>> { @@ -140,6 +173,18 @@ LDAP; } + /** Common UUID assigned to the "Project Owners" placeholder group. */ + public static final AccountGroup.UUID PROJECT_OWNERS = + new AccountGroup.UUID("global:Project-Owners"); + + /** Common UUID assigned to the "Anonymous Users" group. */ + public static final AccountGroup.UUID ANONYMOUS_USERS = + new AccountGroup.UUID("global:Anonymous-Users"); + + /** Common UUID assigned to the "Registered Users" group. */ + public static final AccountGroup.UUID REGISTERED_USERS = + new AccountGroup.UUID("global:Registered-Users"); + /** Unique name of this group within the system. */ @Column(id = 1) protected NameKey name; @@ -176,15 +221,20 @@ @Column(id = 8) protected boolean emailOnlyAuthors; + /** Globally unique identifier name for this group. */ + @Column(id = 9) + protected UUID groupUUID; + protected AccountGroup() { } public AccountGroup(final AccountGroup.NameKey newName, - final AccountGroup.Id newId) { + final AccountGroup.Id newId, final AccountGroup.UUID uuid) { name = newName; groupId = newId; ownerGroupId = groupId; visibleToAll = false; + groupUUID = uuid; setType(Type.INTERNAL); } @@ -251,4 +301,12 @@ public void setEmailOnlyAuthors(boolean emailOnlyAuthors) { this.emailOnlyAuthors = emailOnlyAuthors; } + + public AccountGroup.UUID getGroupUUID() { + return groupUUID; + } + + public void setGroupUUID(AccountGroup.UUID uuid) { + groupUUID = uuid; + } }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAccess.java index 2530654..7eb7ed2 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAccess.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/AccountGroupAccess.java
@@ -25,6 +25,9 @@ @PrimaryKey("groupId") AccountGroup get(AccountGroup.Id id) throws OrmException; + @Query("WHERE groupUUID = ?") + ResultSet<AccountGroup> byUUID(AccountGroup.UUID uuid) throws OrmException; + @Query("WHERE externalName = ?") ResultSet<AccountGroup> byExternalName(AccountGroup.ExternalNameKey name) throws OrmException;
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategory.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategory.java index 29ea97e..d7e5024 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategory.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ApprovalCategory.java
@@ -24,33 +24,6 @@ public static final ApprovalCategory.Id SUBMIT = new ApprovalCategory.Id("SUBM"); - /** Id of the special "Read" action (and category). */ - public static final ApprovalCategory.Id READ = - new ApprovalCategory.Id("READ"); - - /** Id of the special "Own" category; manages a project. */ - public static final ApprovalCategory.Id OWN = new ApprovalCategory.Id("OWN"); - - /** Id of the special "Push Annotated Tag" action (and category). */ - public static final ApprovalCategory.Id PUSH_TAG = - new ApprovalCategory.Id("pTAG"); - public static final short PUSH_TAG_SIGNED = 1; - public static final short PUSH_TAG_ANNOTATED = 2; - - /** Id of the special "Push Branch" action (and category). */ - public static final ApprovalCategory.Id PUSH_HEAD = - new ApprovalCategory.Id("pHD"); - public static final short PUSH_HEAD_UPDATE = 1; - public static final short PUSH_HEAD_CREATE = 2; - public static final short PUSH_HEAD_REPLACE = 3; - - /** Id of the special "Forge Identity" category. */ - public static final ApprovalCategory.Id FORGE_IDENTITY = - new ApprovalCategory.Id("FORG"); - public static final short FORGE_AUTHOR = 1; - public static final short FORGE_COMMITTER = 2; - public static final short FORGE_SERVER = 3; - public static class Id extends StringKey<Key<?>> { private static final long serialVersionUID = 1L; @@ -73,15 +46,6 @@ protected void set(String newValue) { id = newValue; } - - /** True if the right can be assigned on the wild project. */ - public boolean canBeOnWildProject() { - if (OWN.equals(this)) { - return false; - } else { - return true; - } - } } /** Internal short unique identifier for this category. */ @@ -96,16 +60,7 @@ @Column(id = 3, length = 4, notNull = false) protected String abbreviatedName; - /** - * Order of this category within the Approvals table when presented. - * <p> - * If < 0 (e.g. -1) this category is not shown in the Approvals table but is - * instead considered to be an action that the user might be able to perform, - * e.g. "Submit". - * <p> - * If >= 0 this category is shown in the Approvals table, sorted along with - * its siblings by <code>position, name</code>. - */ + /** Order of this category within the Approvals table when presented. */ @Column(id = 4) protected short position; @@ -117,6 +72,9 @@ @Column(id = 6) protected boolean copyMinScore; + /** Computed name derived from {@link #name}. */ + protected String labelName; + protected ApprovalCategory() { } @@ -136,6 +94,26 @@ public void setName(final String n) { name = n; + labelName = null; + } + + /** Clean version of {@link #getName()}, e.g. "Code Review" is "Code-Review". */ + public String getLabelName() { + if (labelName == null) { + StringBuilder r = new StringBuilder(); + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if (('0' <= c && c <= '9') // + || ('a' <= c && c <= 'z') // + || ('A' <= c && c <= 'Z')) { + r.append(c); + } else if (c == ' ') { + r.append('-'); + } + } + labelName = r.toString(); + } + return labelName; } public String getAbbreviatedName() { @@ -154,14 +132,6 @@ position = p; } - public boolean isAction() { - return position < 0; - } - - public boolean isRange() { - return !isAction(); - } - public String getFunctionName() { return functionName; }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Project.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Project.java index 409547a..49f3333 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Project.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/Project.java
@@ -21,7 +21,7 @@ public final class Project { /** Project name key */ public static class NameKey extends - StringKey<com.google.gwtorm.client.Key<?>> { + StringKey<com.google.gwtorm.client.Key<?>> implements Comparable<NameKey> { private static final long serialVersionUID = 1L; @Column(id = 1) @@ -44,6 +44,11 @@ name = newValue; } + @Override + public int compareTo(NameKey other) { + return get().compareTo(other.get()); + } + /** Parse a Project.NameKey out of a string representation. */ public static NameKey parse(final String str) { final NameKey r = new NameKey(); @@ -53,65 +58,37 @@ } public static enum SubmitType { - FAST_FORWARD_ONLY('F'), + FAST_FORWARD_ONLY, - MERGE_IF_NECESSARY('M'), + MERGE_IF_NECESSARY, - MERGE_ALWAYS('A'), + MERGE_ALWAYS, - CHERRY_PICK('C'); - - private final char code; - - private SubmitType(final char c) { - code = c; - } - - public char getCode() { - return code; - } - - public static SubmitType forCode(final char c) { - for (final SubmitType s : SubmitType.values()) { - if (s.code == c) { - return s; - } - } - return null; - } + CHERRY_PICK; } - @Column(id = 1) protected NameKey name; - @Column(id = 2, length = Integer.MAX_VALUE, notNull = false) protected String description; - @Column(id = 3) protected boolean useContributorAgreements; - @Column(id = 4) protected boolean useSignedOffBy; - @Column(id = 5) - protected char submitType; + protected SubmitType submitType; - @Column(id = 6, notNull = false, name = "parent_name") protected NameKey parent; - @Column(id = 7) protected boolean requireChangeID; - @Column(id = 8) protected boolean useContentMerge; protected Project() { } - public Project(final Project.NameKey newName) { - name = newName; - useContributorAgreements = true; - setSubmitType(SubmitType.MERGE_IF_NECESSARY); + public Project(Project.NameKey nameKey) { + name = nameKey; + submitType = SubmitType.MERGE_IF_NECESSARY; } public Project.NameKey getNameKey() { @@ -119,7 +96,7 @@ } public String getName() { - return name.get(); + return name != null ? name.get() : null; } public String getDescription() { @@ -163,11 +140,11 @@ } public SubmitType getSubmitType() { - return SubmitType.forCode(submitType); + return submitType; } public void setSubmitType(final SubmitType type) { - submitType = type.getCode(); + submitType = type; } public void copySettingsFrom(final Project update) { @@ -183,7 +160,11 @@ return parent; } - public void setParent(final Project.NameKey parentProjectName) { - parent = parentProjectName; + public String getParentName() { + return parent != null ? parent.get() : null; + } + + public void setParentName(String n) { + parent = n != null ? new NameKey(n) : null; } }
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectAccess.java deleted file mode 100644 index b9adada..0000000 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ProjectAccess.java +++ /dev/null
@@ -1,33 +0,0 @@ -// Copyright (C) 2008 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.reviewdb; - -import com.google.gwtorm.client.Access; -import com.google.gwtorm.client.OrmException; -import com.google.gwtorm.client.PrimaryKey; -import com.google.gwtorm.client.Query; -import com.google.gwtorm.client.ResultSet; - -public interface ProjectAccess extends Access<Project, Project.NameKey> { - @PrimaryKey("name") - Project get(Project.NameKey name) throws OrmException; - - @Query("ORDER BY name") - ResultSet<Project> all() throws OrmException; - - @Query("WHERE name.name >= ? AND name.name <= ? ORDER BY name LIMIT ?") - ResultSet<Project> suggestByName(String nameA, String nameB, int limit) - throws OrmException; -}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/RefRight.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/RefRight.java deleted file mode 100644 index 97ee219..0000000 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/RefRight.java +++ /dev/null
@@ -1,235 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.reviewdb; - -import com.google.gwtorm.client.Column; -import com.google.gwtorm.client.CompoundKey; -import com.google.gwtorm.client.StringKey; - -import java.util.Comparator; - -/** Grant to use an {@link ApprovalCategory} in the scope of a git ref. */ -public final class RefRight { - /** Pattern that matches all references in a project. */ - public static final String ALL = "refs/*"; - - /** Prefix that triggers a regular expression pattern. */ - public static final String REGEX_PREFIX = "^"; - - public static class RefPattern extends - StringKey<com.google.gwtorm.client.Key<?>> { - private static final long serialVersionUID = 1L; - - @Column(id = 1) - protected String pattern; - - protected RefPattern() { - } - - public RefPattern(final String pattern) { - this.pattern = pattern; - } - - @Override - public String get() { - return pattern; - } - - @Override - protected void set(String pattern) { - this.pattern = pattern; - } - } - - public static class Key extends CompoundKey<Project.NameKey> { - private static final long serialVersionUID = 1L; - - @Column(id = 1) - protected Project.NameKey projectName; - - @Column(id = 2) - protected RefPattern refPattern; - - @Column(id = 3) - protected ApprovalCategory.Id categoryId; - - @Column(id = 4) - protected AccountGroup.Id groupId; - - protected Key() { - projectName = new Project.NameKey(); - refPattern = new RefPattern(); - categoryId = new ApprovalCategory.Id(); - groupId = new AccountGroup.Id(); - } - - public Key(final Project.NameKey projectName, final RefPattern refPattern, - final ApprovalCategory.Id categoryId, final AccountGroup.Id groupId) { - this.projectName = projectName; - this.refPattern = refPattern; - this.categoryId = categoryId; - this.groupId = groupId; - } - - @Override - public Project.NameKey getParentKey() { - return projectName; - } - - public Project.NameKey getProjectNameKey() { - return projectName; - } - - public String getRefPattern() { - return refPattern.get(); - } - - public void setGroupId(AccountGroup.Id groupId) { - this.groupId = groupId; - } - - @Override - public com.google.gwtorm.client.Key<?>[] members() { - return new com.google.gwtorm.client.Key<?>[] {refPattern, categoryId, - groupId}; - } - } - - @Column(id = 1, name = Column.NONE) - protected Key key; - - @Column(id = 2) - protected short minValue; - - @Column(id = 3) - protected short maxValue; - - protected RefRight() { - } - - public RefRight(RefRight.Key key) { - this.key = key; - } - - public RefRight(final RefRight refRight, final AccountGroup.Id groupId) { - this(new RefRight.Key(refRight.getKey().projectName, - refRight.getKey().refPattern, refRight.getKey().categoryId, groupId)); - setMinValue(refRight.getMinValue()); - setMaxValue(refRight.getMaxValue()); - } - - public RefRight.Key getKey() { - return key; - } - - public String getRefPattern() { - if (isExclusive()) { - return key.refPattern.get().substring(1); - } - return key.refPattern.get(); - } - - public String getRefPatternForDisplay() { - return key.refPattern.get(); - } - - public Project.NameKey getProjectNameKey() { - return getKey().getProjectNameKey(); - } - - public boolean isExclusive() { - return key.refPattern.get().startsWith("-"); - } - - public ApprovalCategory.Id getApprovalCategoryId() { - return key.categoryId; - } - - public AccountGroup.Id getAccountGroupId() { - return key.groupId; - } - - public short getMinValue() { - return minValue; - } - - public void setMinValue(final short m) { - minValue = m; - } - - public short getMaxValue() { - return maxValue; - } - - public void setMaxValue(final short m) { - maxValue = m; - } - - @Override - public String toString() { - StringBuilder s = new StringBuilder(); - s.append("{group :"); - s.append(getAccountGroupId().get()); - s.append(", proj :"); - s.append(getProjectNameKey().get()); - s.append(", cat :"); - s.append(getApprovalCategoryId().get()); - s.append(", pattern :"); - s.append(getRefPatternForDisplay()); - s.append(", min :"); - s.append(getMinValue()); - s.append(", max :"); - s.append(getMaxValue()); - s.append("}"); - return s.toString(); - } - - @Override - public int hashCode() { - return getKey().hashCode(); - } - - @Override - public boolean equals(Object o) { - if (o instanceof RefRight) { - RefRight a = this; - RefRight b = (RefRight) o; - return a.getKey().equals(b.getKey()) - && a.getMinValue() == b.getMinValue() - && a.getMaxValue() == b.getMaxValue(); - } - return false; - } - - public static final Comparator<RefRight> REF_PATTERN_ORDER = - new Comparator<RefRight>() { - - @Override - public int compare(RefRight a, RefRight b) { - int aLength = a.getRefPattern().length(); - int bLength = b.getRefPattern().length(); - if (bLength == aLength) { - ApprovalCategory.Id aCat = a.getApprovalCategoryId(); - ApprovalCategory.Id bCat = b.getApprovalCategoryId(); - if (aCat.get().equals(bCat.get())) { - return a.getRefPattern().compareTo(b.getRefPattern()); - } - return a.getApprovalCategoryId().get() - .compareTo(b.getApprovalCategoryId().get()); - } - return bLength - aLength; - } - }; -}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/RefRightAccess.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/RefRightAccess.java deleted file mode 100644 index a42ff2c..0000000 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/RefRightAccess.java +++ /dev/null
@@ -1,33 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.reviewdb; - -import com.google.gwtorm.client.Access; -import com.google.gwtorm.client.OrmException; -import com.google.gwtorm.client.PrimaryKey; -import com.google.gwtorm.client.Query; -import com.google.gwtorm.client.ResultSet; - -public interface RefRightAccess extends Access<RefRight, RefRight.Key> { - @PrimaryKey("key") - RefRight get(RefRight.Key refRight) throws OrmException; - - @Query("WHERE key.projectName = ?") - ResultSet<RefRight> byProject(Project.NameKey project) throws OrmException; - - @Query("WHERE key.categoryId = ? AND key.groupId = ?") - ResultSet<RefRight> byCategoryGroup(ApprovalCategory.Id cat, - AccountGroup.Id group) throws OrmException; -}
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ReviewDb.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ReviewDb.java index 49e07ff..b75b91b 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ReviewDb.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/ReviewDb.java
@@ -24,7 +24,6 @@ * <p> * Root entities that are at the top level of some important data graph: * <ul> - * <li>{@link Project}: Configuration for a single Git repository.</li> * <li>{@link Account}: Per-user account registration, preferences, identity.</li> * <li>{@link Change}: All review information about a single proposed change.</li> * <li>{@link SystemConfig}: Server-wide settings, managed by administrator.</li> @@ -94,9 +93,6 @@ AccountPatchReviewAccess accountPatchReviews(); @Relation - ProjectAccess projects(); - - @Relation ChangeAccess changes(); @Relation @@ -115,9 +111,6 @@ PatchLineCommentAccess patchComments(); @Relation - RefRightAccess refRights(); - - @Relation TrackingIdAccess trackingIds(); /** Create the next unique id for an {@link Account}. */
diff --git a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfig.java b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfig.java index 6ff23ed..229d173 100644 --- a/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfig.java +++ b/gerrit-reviewdb/src/main/java/com/google/gerrit/reviewdb/SystemConfig.java
@@ -65,6 +65,8 @@ /** Identity of the administration group; those with full access. */ @Column(id = 4) public AccountGroup.Id adminGroupId; + @Column(id = 10) + public AccountGroup.UUID adminGroupUUID; /** Identity of the anonymous group, which permits anyone. */ @Column(id = 5) @@ -81,6 +83,8 @@ /** Identity of the batch users group */ @Column(id = 8) public AccountGroup.Id batchUsersGroupId; + @Column(id = 11) + public AccountGroup.UUID batchUsersGroupUUID; /** Identity of the owner group, which permits any project owner. */ @Column(id = 9)
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/index_generic.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/index_generic.sql index d33d24d..9784a4d 100644 --- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/index_generic.sql +++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/index_generic.sql
@@ -158,14 +158,6 @@ -- ********************************************************************* --- RefRightAccess --- @PrimaryKey covers: byProject --- covers: byCategoryGroup -CREATE INDEX ref_rights_byCatGroup -ON ref_rights (category_id, group_id); - - --- ********************************************************************* -- TrackingIdAccess -- CREATE INDEX tracking_ids_byTrkId
diff --git a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/index_postgres.sql b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/index_postgres.sql index 8e3cead..db6894d 100644 --- a/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/index_postgres.sql +++ b/gerrit-reviewdb/src/main/resources/com/google/gerrit/reviewdb/index_postgres.sql
@@ -240,14 +240,6 @@ -- ********************************************************************* --- RefRightAccess --- @PrimaryKey covers: byProject --- covers: byCategoryGroup -CREATE INDEX ref_rights_byCatGroup -ON ref_rights (category_id, group_id); - - --- ********************************************************************* -- TrackingIdAccess -- CREATE INDEX tracking_ids_byTrkId
diff --git a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java index 1a4a863..8205946 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java +++ b/gerrit-server/src/main/java/com/google/gerrit/common/ChangeHookRunner.java
@@ -433,8 +433,10 @@ Entry<ApprovalCategory.Id, ApprovalCategoryValue.Id> approval) { ApprovalAttribute a = new ApprovalAttribute(); a.type = approval.getKey().get(); - final ApprovalType at = approvalTypes.getApprovalType(approval.getKey()); - a.description = at.getCategory().getName(); + ApprovalType at = approvalTypes.byId(approval.getKey()); + if (at != null) { + a.description = at.getCategory().getName(); + } a.value = Short.toString(approval.getValue().get()); return a; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/AnonymousUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/AnonymousUser.java index 1bd2066..3a450ed 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/AnonymousUser.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/AnonymousUser.java
@@ -34,8 +34,8 @@ } @Override - public Set<AccountGroup.Id> getEffectiveGroups() { - return authConfig.getAnonymousGroups(); + public Set<AccountGroup.UUID> getEffectiveGroups() { + return Collections.singleton(AccountGroup.ANONYMOUS_USERS); } @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/CurrentUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/CurrentUser.java index 0d8d19a..512de00 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/CurrentUser.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/CurrentUser.java
@@ -56,7 +56,7 @@ * * @return active groups for this user. */ - public abstract Set<AccountGroup.Id> getEffectiveGroups(); + public abstract Set<AccountGroup.UUID> getEffectiveGroups(); /** Set of changes starred by this user. */ public abstract Set<Change.Id> getStarredChanges();
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java index e46957d..5fb5a7f 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/IdentifiedUser.java
@@ -43,11 +43,14 @@ import java.net.MalformedURLException; import java.net.SocketAddress; import java.net.URL; +import java.util.AbstractSet; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.LinkedList; +import java.util.Iterator; import java.util.List; import java.util.Queue; import java.util.Set; @@ -139,6 +142,28 @@ private static final Logger log = LoggerFactory.getLogger(IdentifiedUser.class); + private static final Set<AccountGroup.UUID> registeredGroups = + new AbstractSet<AccountGroup.UUID>() { + private final List<AccountGroup.UUID> groups = + Collections.unmodifiableList(Arrays.asList(new AccountGroup.UUID[] { + AccountGroup.ANONYMOUS_USERS, AccountGroup.REGISTERED_USERS})); + + @Override + public boolean contains(Object o) { + return groups.contains(o); + } + + @Override + public Iterator<AccountGroup.UUID> iterator() { + return groups.iterator(); + } + + @Override + public int size() { + return groups.size(); + } + }; + private final Provider<String> canonicalUrl; private final Realm realm; private final AccountCache accountCache; @@ -154,7 +179,7 @@ private AccountState state; private Set<String> emailAddresses; - private Set<AccountGroup.Id> effectiveGroups; + private Set<AccountGroup.UUID> effectiveGroups; private Set<Change.Id> starredChanges; private Collection<AccountProjectWatch> notificationFilters; @@ -217,14 +242,14 @@ } @Override - public Set<AccountGroup.Id> getEffectiveGroups() { + public Set<AccountGroup.UUID> getEffectiveGroups() { if (effectiveGroups == null) { - Set<AccountGroup.Id> seedGroups; + Set<AccountGroup.UUID> seedGroups; if (authConfig.isIdentityTrustable(state().getExternalIds())) { seedGroups = realm.groups(state()); } else { - seedGroups = authConfig.getRegisteredGroups(); + seedGroups = registeredGroups; } effectiveGroups = getIncludedGroups(seedGroups); @@ -233,14 +258,14 @@ return effectiveGroups; } - private Set<AccountGroup.Id> getIncludedGroups(Set<AccountGroup.Id> seedGroups) { - Set<AccountGroup.Id> includes = new HashSet<AccountGroup.Id> (seedGroups); - Queue<AccountGroup.Id> groupQueue = new LinkedList<AccountGroup.Id> (seedGroups); + private Set<AccountGroup.UUID> getIncludedGroups(Set<AccountGroup.UUID> seedGroups) { + Set<AccountGroup.UUID> includes = new HashSet<AccountGroup.UUID> (seedGroups); + Queue<AccountGroup.UUID> groupQueue = new LinkedList<AccountGroup.UUID> (seedGroups); while (groupQueue.size() > 0) { - AccountGroup.Id id = groupQueue.remove(); + AccountGroup.UUID id = groupQueue.remove(); - for (final AccountGroup.Id groupId : groupIncludeCache.getByInclude(id)) { + for (final AccountGroup.UUID groupId : groupIncludeCache.getByInclude(id)) { if (includes.add(groupId)) { groupQueue.add(groupId); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/PeerDaemonUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/PeerDaemonUser.java index 26dec09..a4a72a7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/PeerDaemonUser.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/PeerDaemonUser.java
@@ -36,21 +36,21 @@ PeerDaemonUser create(@Assisted SocketAddress peer); } - private final Set<AccountGroup.Id> effectiveGroups; + private final Set<AccountGroup.UUID> effectiveGroups; private final SocketAddress peer; @Inject protected PeerDaemonUser(AuthConfig authConfig, @Assisted SocketAddress peer) { super(AccessPath.SSH_COMMAND, authConfig); - final HashSet<AccountGroup.Id> g = new HashSet<AccountGroup.Id>(); + final HashSet<AccountGroup.UUID> g = new HashSet<AccountGroup.UUID>(); g.add(authConfig.getAdministratorsGroup()); this.effectiveGroups = Collections.unmodifiableSet(g); this.peer = peer; } @Override - public Set<AccountGroup.Id> getEffectiveGroups() { + public Set<AccountGroup.UUID> getEffectiveGroups() { return effectiveGroups; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/ReplicationUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/ReplicationUser.java index 5c44bfe..afb8800 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/ReplicationUser.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/ReplicationUser.java
@@ -28,18 +28,18 @@ public class ReplicationUser extends CurrentUser { /** Magic set of groups enabling read of any project and reference. */ - public static final Set<AccountGroup.Id> EVERYTHING_VISIBLE = - Collections.unmodifiableSet(new HashSet<AccountGroup.Id>(0)); + public static final Set<AccountGroup.UUID> EVERYTHING_VISIBLE = + Collections.unmodifiableSet(new HashSet<AccountGroup.UUID>(0)); public interface Factory { - ReplicationUser create(@Assisted Set<AccountGroup.Id> authGroups); + ReplicationUser create(@Assisted Set<AccountGroup.UUID> authGroups); } - private final Set<AccountGroup.Id> effectiveGroups; + private final Set<AccountGroup.UUID> effectiveGroups; @Inject protected ReplicationUser(AuthConfig authConfig, - @Assisted Set<AccountGroup.Id> authGroups) { + @Assisted Set<AccountGroup.UUID> authGroups) { super(AccessPath.REPLICATION, authConfig); if (authGroups == EVERYTHING_VISIBLE) { @@ -53,12 +53,12 @@ } } - private static Set<AccountGroup.Id> copy(Set<AccountGroup.Id> groups) { - return Collections.unmodifiableSet(new HashSet<AccountGroup.Id>(groups)); + private static Set<AccountGroup.UUID> copy(Set<AccountGroup.UUID> groups) { + return Collections.unmodifiableSet(new HashSet<AccountGroup.UUID>(groups)); } @Override - public Set<AccountGroup.Id> getEffectiveGroups() { + public Set<AccountGroup.UUID> getEffectiveGroups() { return effectiveGroups; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java index 52ccc66..aea13c7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountCacheImpl.java
@@ -22,7 +22,6 @@ import com.google.gerrit.server.cache.Cache; import com.google.gerrit.server.cache.CacheModule; import com.google.gerrit.server.cache.EntryCreator; -import com.google.gerrit.server.config.AuthConfig; import com.google.gwtorm.client.OrmException; import com.google.gwtorm.client.SchemaFactory; import com.google.inject.Inject; @@ -90,18 +89,13 @@ static class ByIdLoader extends EntryCreator<Account.Id, AccountState> { private final SchemaFactory<ReviewDb> schema; - private final Set<AccountGroup.Id> registered; - private final Set<AccountGroup.Id> anonymous; private final GroupCache groupCache; private final Cache<String, Account.Id> byName; @Inject - ByIdLoader(SchemaFactory<ReviewDb> sf, AuthConfig auth, - GroupCache groupCache, + ByIdLoader(SchemaFactory<ReviewDb> sf, GroupCache groupCache, @Named(BYUSER_NAME) Cache<String, Account.Id> byUsername) { this.schema = sf; - this.registered = auth.getRegisteredGroups(); - this.anonymous = auth.getAnonymousGroups(); this.groupCache = groupCache; this.byName = byUsername; } @@ -133,21 +127,18 @@ Collections.unmodifiableCollection(db.accountExternalIds().byAccount( who).toList()); - Set<AccountGroup.Id> internalGroups = new HashSet<AccountGroup.Id>(); + Set<AccountGroup.UUID> internalGroups = new HashSet<AccountGroup.UUID>(); for (AccountGroupMember g : db.accountGroupMembers().byAccount(who)) { final AccountGroup.Id groupId = g.getAccountGroupId(); final AccountGroup group = groupCache.get(groupId); if (group != null && group.getType() == AccountGroup.Type.INTERNAL) { - internalGroups.add(groupId); + internalGroups.add(group.getGroupUUID()); } } - if (internalGroups.isEmpty()) { - internalGroups = registered; - } else { - internalGroups.addAll(registered); - internalGroups = Collections.unmodifiableSet(internalGroups); - } + internalGroups.add(AccountGroup.REGISTERED_USERS); + internalGroups.add(AccountGroup.ANONYMOUS_USERS); + internalGroups = Collections.unmodifiableSet(internalGroups); return new AccountState(account, internalGroups, externalIds); } @@ -156,6 +147,8 @@ public AccountState missing(final Account.Id accountId) { final Account account = new Account(accountId); final Collection<AccountExternalId> ids = Collections.emptySet(); + final Set<AccountGroup.UUID> anonymous = + Collections.singleton(AccountGroup.ANONYMOUS_USERS); return new AccountState(account, anonymous, ids); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java index 5cb8f36..a388eef 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountManager.java
@@ -275,7 +275,9 @@ // is going to be the site's administrator and just make them that // to bootstrap the authentication database. // - final AccountGroup.Id admin = authConfig.getAdministratorsGroup(); + final AccountGroup.UUID uuid = authConfig.getAdministratorsGroup(); + final AccountGroup g = db.accountGroups().byUUID(uuid).iterator().next(); + final AccountGroup.Id admin = g.getId(); final AccountGroupMember m = new AccountGroupMember(new AccountGroupMember.Key(newId, admin)); db.accountGroupMembersAudit().insert(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountState.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountState.java index 9393227..1b036d8 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountState.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/AccountState.java
@@ -26,11 +26,11 @@ public class AccountState { private final Account account; - private final Set<AccountGroup.Id> internalGroups; + private final Set<AccountGroup.UUID> internalGroups; private final Collection<AccountExternalId> externalIds; public AccountState(final Account account, - final Set<AccountGroup.Id> actualGroups, + final Set<AccountGroup.UUID> actualGroups, final Collection<AccountExternalId> externalIds) { this.account = account; this.internalGroups = actualGroups; @@ -89,7 +89,7 @@ } /** The set of groups maintained directly within the Gerrit database. */ - public Set<AccountGroup.Id> getInternalGroups() { + public Set<AccountGroup.UUID> getInternalGroups() { return internalGroups; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java index a836f54..77afb13 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/DefaultRealm.java
@@ -51,7 +51,7 @@ } @Override - public Set<AccountGroup.Id> groups(final AccountState who) { + public Set<AccountGroup.UUID> groups(final AccountState who) { return who.getInternalGroups(); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCache.java index 978d9c2..6dce197 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCache.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCache.java
@@ -24,6 +24,8 @@ public AccountGroup get(AccountGroup.NameKey name); + public AccountGroup get(AccountGroup.UUID uuid); + public Collection<AccountGroup> get(AccountGroup.ExternalNameKey externalName); public void evict(AccountGroup group);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java index d948aef..4d6dbc1 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupCacheImpl.java
@@ -20,7 +20,6 @@ import com.google.gerrit.server.cache.Cache; import com.google.gerrit.server.cache.CacheModule; import com.google.gerrit.server.cache.EntryCreator; -import com.google.gerrit.server.config.AuthConfig; import com.google.gwtorm.client.SchemaFactory; import com.google.inject.Inject; import com.google.inject.Module; @@ -29,12 +28,14 @@ import com.google.inject.name.Named; import java.util.Collection; +import java.util.List; /** Tracks group objects in memory for efficient access. */ @Singleton public class GroupCacheImpl implements GroupCache { private static final String BYID_NAME = "groups"; private static final String BYNAME_NAME = "groups_byname"; + private static final String BYUUID_NAME = "groups_byuuid"; private static final String BYEXT_NAME = "groups_byext"; public static Module module() { @@ -49,6 +50,10 @@ new TypeLiteral<Cache<AccountGroup.NameKey, AccountGroup>>() {}; core(byName, BYNAME_NAME).populateWith(ByNameLoader.class); + final TypeLiteral<Cache<AccountGroup.UUID, AccountGroup>> byUUID = + new TypeLiteral<Cache<AccountGroup.UUID, AccountGroup>>() {}; + core(byUUID, BYUUID_NAME).populateWith(ByUUIDLoader.class); + final TypeLiteral<Cache<AccountGroup.ExternalNameKey, Collection<AccountGroup>>> byExternalName = new TypeLiteral<Cache<AccountGroup.ExternalNameKey, Collection<AccountGroup>>>() {}; core(byExternalName, BYEXT_NAME) // @@ -62,15 +67,18 @@ private final Cache<AccountGroup.Id, AccountGroup> byId; private final Cache<AccountGroup.NameKey, AccountGroup> byName; + private final Cache<AccountGroup.UUID, AccountGroup> byUUID; private final Cache<AccountGroup.ExternalNameKey, Collection<AccountGroup>> byExternalName; @Inject GroupCacheImpl( @Named(BYID_NAME) Cache<AccountGroup.Id, AccountGroup> byId, @Named(BYNAME_NAME) Cache<AccountGroup.NameKey, AccountGroup> byName, + @Named(BYUUID_NAME) Cache<AccountGroup.UUID, AccountGroup> byUUID, @Named(BYEXT_NAME) Cache<AccountGroup.ExternalNameKey, Collection<AccountGroup>> byExternalName) { this.byId = byId; this.byName = byName; + this.byUUID = byUUID; this.byExternalName = byExternalName; } @@ -81,6 +89,7 @@ public void evict(final AccountGroup group) { byId.remove(group.getId()); byName.remove(group.getNameKey()); + byUUID.remove(group.getGroupUUID()); byExternalName.remove(group.getExternalNameKey()); } @@ -92,6 +101,10 @@ return byName.get(name); } + public AccountGroup get(final AccountGroup.UUID uuid) { + return byUUID.get(uuid); + } + public Collection<AccountGroup> get( final AccountGroup.ExternalNameKey externalName) { return byExternalName.get(externalName); @@ -99,12 +112,10 @@ static class ByIdLoader extends EntryCreator<AccountGroup.Id, AccountGroup> { private final SchemaFactory<ReviewDb> schema; - private final AccountGroup.Id administrators; @Inject - ByIdLoader(final SchemaFactory<ReviewDb> sf, final AuthConfig authConfig) { + ByIdLoader(final SchemaFactory<ReviewDb> sf) { schema = sf; - administrators = authConfig.getAdministratorsGroup(); } @Override @@ -126,9 +137,8 @@ public AccountGroup missing(final AccountGroup.Id key) { final AccountGroup.NameKey name = new AccountGroup.NameKey("Deleted Group" + key.toString()); - final AccountGroup g = new AccountGroup(name, key); + final AccountGroup g = new AccountGroup(name, key, null); g.setType(AccountGroup.Type.SYSTEM); - g.setOwnerGroupId(administrators); return g; } } @@ -160,6 +170,32 @@ } } + static class ByUUIDLoader extends + EntryCreator<AccountGroup.UUID, AccountGroup> { + private final SchemaFactory<ReviewDb> schema; + + @Inject + ByUUIDLoader(final SchemaFactory<ReviewDb> sf) { + schema = sf; + } + + @Override + public AccountGroup createEntry(final AccountGroup.UUID uuid) + throws Exception { + final ReviewDb db = schema.open(); + try { + List<AccountGroup> r = db.accountGroups().byUUID(uuid).toList(); + if (r.size() == 1) { + return r.get(0); + } else { + return null; + } + } finally { + db.close(); + } + } + } + static class ByExternalNameLoader extends EntryCreator<AccountGroup.ExternalNameKey, Collection<AccountGroup>> { private final SchemaFactory<ReviewDb> schema;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java index 602b593..96c8df9 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupControl.java
@@ -42,6 +42,15 @@ return new GroupControl(user.get(), group); } + public GroupControl controlFor(final AccountGroup.UUID groupId) + throws NoSuchGroupException { + final AccountGroup group = groupCache.get(groupId); + if (group == null) { + throw new NoSuchGroupException(groupId); + } + return new GroupControl(user.get(), group); + } + public GroupControl controlFor(final AccountGroup group) { return new GroupControl(user.get(), group); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCache.java index 3088806..e5f73a3 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCache.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCache.java
@@ -20,8 +20,8 @@ /** Tracks group inclusions in memory for efficient access. */ public interface GroupIncludeCache { - public Collection<AccountGroup.Id> getByInclude(AccountGroup.Id groupId); + public Collection<AccountGroup.UUID> getByInclude(AccountGroup.UUID groupId); - public void evictInclude(AccountGroup.Id groupId); + public void evictInclude(AccountGroup.UUID groupId); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java index 76e9231..830d01c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupIncludeCacheImpl.java
@@ -21,16 +21,17 @@ import com.google.gerrit.server.cache.CacheModule; import com.google.gerrit.server.cache.EntryCreator; import com.google.gwtorm.client.SchemaFactory; - import com.google.inject.Inject; import com.google.inject.Module; import com.google.inject.Singleton; import com.google.inject.TypeLiteral; import com.google.inject.name.Named; -import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; /** Tracks group inclusions in memory for efficient access. */ @Singleton @@ -41,8 +42,8 @@ return new CacheModule() { @Override protected void configure() { - final TypeLiteral<Cache<AccountGroup.Id, Collection<AccountGroup.Id>>> byInclude = - new TypeLiteral<Cache<AccountGroup.Id, Collection<AccountGroup.Id>>>() {}; + final TypeLiteral<Cache<AccountGroup.UUID, Collection<AccountGroup.UUID>>> byInclude = + new TypeLiteral<Cache<AccountGroup.UUID, Collection<AccountGroup.UUID>>>() {}; core(byInclude, BYINCLUDE_NAME).populateWith(ByIncludeLoader.class); bind(GroupIncludeCacheImpl.class); @@ -51,23 +52,24 @@ }; } - private final Cache<AccountGroup.Id, Collection<AccountGroup.Id>> byInclude; + private final Cache<AccountGroup.UUID, Collection<AccountGroup.UUID>> byInclude; @Inject GroupIncludeCacheImpl( - @Named(BYINCLUDE_NAME) Cache<AccountGroup.Id, Collection<AccountGroup.Id>> byInclude) { + @Named(BYINCLUDE_NAME) Cache<AccountGroup.UUID, Collection<AccountGroup.UUID>> byInclude) { this.byInclude = byInclude; } - public Collection<AccountGroup.Id> getByInclude(final AccountGroup.Id groupId) { + public Collection<AccountGroup.UUID> getByInclude(AccountGroup.UUID groupId) { return byInclude.get(groupId); } - public void evictInclude(AccountGroup.Id groupId) { + public void evictInclude(AccountGroup.UUID groupId) { byInclude.remove(groupId); } - static class ByIncludeLoader extends EntryCreator<AccountGroup.Id, Collection<AccountGroup.Id>> { + static class ByIncludeLoader extends + EntryCreator<AccountGroup.UUID, Collection<AccountGroup.UUID>> { private final SchemaFactory<ReviewDb> schema; @Inject @@ -76,14 +78,23 @@ } @Override - public Collection<AccountGroup.Id> createEntry(final AccountGroup.Id key) throws Exception { + public Collection<AccountGroup.UUID> createEntry(final AccountGroup.UUID key) throws Exception { final ReviewDb db = schema.open(); try { - ArrayList<AccountGroup.Id> groupArray = new ArrayList<AccountGroup.Id> (); - for (AccountGroupInclude agi : db.accountGroupIncludes().byInclude(key)) { - groupArray.add(agi.getGroupId()); + List<AccountGroup> group = db.accountGroups().byUUID(key).toList(); + if (group.size() != 1) { + return Collections.emptyList(); } + Set<AccountGroup.Id> ids = new HashSet<AccountGroup.Id>(); + for (AccountGroupInclude agi : db.accountGroupIncludes().byInclude(group.get(0).getId())) { + ids.add(agi.getGroupId()); + } + + Set<AccountGroup.UUID> groupArray = new HashSet<AccountGroup.UUID> (); + for (AccountGroup g : db.accountGroups().get(ids)) { + groupArray.add(g.getGroupUUID()); + } return Collections.unmodifiableCollection(groupArray); } finally { db.close(); @@ -91,7 +102,7 @@ } @Override - public Collection<AccountGroup.Id> missing(final AccountGroup.Id key) { + public Collection<AccountGroup.UUID> missing(final AccountGroup.UUID key) { return Collections.emptyList(); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupUUID.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupUUID.java new file mode 100644 index 0000000..9eec1df --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/GroupUUID.java
@@ -0,0 +1,35 @@ +// Copyright (C) 2010 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.account; + +import com.google.gerrit.reviewdb.AccountGroup; + +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.PersonIdent; + +import java.security.MessageDigest; + +public class GroupUUID { + public static AccountGroup.UUID make(String groupName, PersonIdent creator) { + MessageDigest md = Constants.newMessageDigest(); + md.update(Constants.encode("group " + groupName + "\n")); + md.update(Constants.encode("creator " + creator.toExternalString() + "\n")); + return new AccountGroup.UUID(ObjectId.fromRaw(md.digest()).name()); + } + + private GroupUUID() { + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java index aab1cda..a017588 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/PerformCreateGroup.java
@@ -23,14 +23,18 @@ import com.google.gerrit.reviewdb.AccountGroupMemberAudit; import com.google.gerrit.reviewdb.AccountGroupName; import com.google.gerrit.reviewdb.ReviewDb; +import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.IdentifiedUser; import com.google.gwtorm.client.OrmDuplicateKeyException; import com.google.gwtorm.client.OrmException; import com.google.inject.Inject; +import org.eclipse.jgit.lib.PersonIdent; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.HashSet; import java.util.List; public class PerformCreateGroup { @@ -43,14 +47,18 @@ private final AccountCache accountCache; private final GroupIncludeCache groupIncludeCache; private final IdentifiedUser currentUser; + private final PersonIdent serverIdent; @Inject PerformCreateGroup(final ReviewDb db, final AccountCache accountCache, - final GroupIncludeCache groupIncludeCache, final IdentifiedUser currentUser) { + final GroupIncludeCache groupIncludeCache, + final IdentifiedUser currentUser, + @GerritPersonIdent final PersonIdent serverIdent) { this.db = db; this.accountCache = accountCache; this.groupIncludeCache = groupIncludeCache; this.currentUser = currentUser; + this.serverIdent = serverIdent; } /** @@ -81,7 +89,11 @@ final AccountGroup.Id groupId = new AccountGroup.Id(db.nextAccountGroupId()); final AccountGroup.NameKey nameKey = new AccountGroup.NameKey(groupName); - final AccountGroup group = new AccountGroup(nameKey, groupId); + final AccountGroup.UUID uuid = GroupUUID.make(groupName, + currentUser.newCommitterIdent( + serverIdent.getWhen(), + serverIdent.getTimeZone())); + final AccountGroup group = new AccountGroup(nameKey, groupId, uuid); group.setVisibleToAll(visibleToAll); if (ownerGroupId != null) { group.setOwnerGroupId(ownerGroupId); @@ -149,8 +161,9 @@ db.accountGroupIncludes().insert(includeList); db.accountGroupIncludesAudit().insert(includesAudit); - for (AccountGroup.Id includeId : groups) { - groupIncludeCache.evictInclude(includeId); + for (AccountGroup group : db.accountGroups().get( + new HashSet<AccountGroup.Id>(groups))) { + groupIncludeCache.evictInclude(group.getGroupUUID()); } } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java b/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java index e42d4ae..072f796 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/account/Realm.java
@@ -27,7 +27,7 @@ public void onCreateAccount(AuthRequest who, Account account); - public Set<AccountGroup.Id> groups(AccountState who); + public Set<AccountGroup.UUID> groups(AccountState who); /** * Locate an account whose local username is the given account name.
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java index 675202c..da4f63a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/Helper.java
@@ -133,7 +133,7 @@ } } - Set<AccountGroup.Id> queryForGroups(final DirContext ctx, + Set<AccountGroup.UUID> queryForGroups(final DirContext ctx, final String username, LdapQuery.Result account) throws NamingException, AccountException { final LdapSchema schema = getSchema(ctx); @@ -175,12 +175,12 @@ } } - final Set<AccountGroup.Id> actual = new HashSet<AccountGroup.Id>(); + final Set<AccountGroup.UUID> actual = new HashSet<AccountGroup.UUID>(); for (String dn : groupDNs) { for (AccountGroup group : groupCache .get(new AccountGroup.ExternalNameKey(dn))) { if (group.getType() == AccountGroup.Type.LDAP) { - actual.add(group.getId()); + actual.add(group.getGroupUUID()); } } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapModule.java index 810df28..d41fe82 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapModule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapModule.java
@@ -32,8 +32,8 @@ @Override protected void configure() { - final TypeLiteral<Cache<String, Set<AccountGroup.Id>>> groups = - new TypeLiteral<Cache<String, Set<AccountGroup.Id>>>() {}; + final TypeLiteral<Cache<String, Set<AccountGroup.UUID>>> groups = + new TypeLiteral<Cache<String, Set<AccountGroup.UUID>>>() {}; core(groups, GROUP_CACHE).maxAge(1, HOURS) // .populateWith(LdapRealm.MemberLoader.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java index de33b44..cd3f2c0 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/auth/ldap/LdapRealm.java
@@ -68,14 +68,14 @@ private final Cache<String, Account.Id> usernameCache; private final Set<Account.FieldName> readOnlyAccountFields; - private final Cache<String, Set<AccountGroup.Id>> membershipCache; + private final Cache<String, Set<AccountGroup.UUID>> membershipCache; @Inject LdapRealm( final Helper helper, final AuthConfig authConfig, final EmailExpander emailExpander, - @Named(LdapModule.GROUP_CACHE) final Cache<String, Set<AccountGroup.Id>> membershipCache, + @Named(LdapModule.GROUP_CACHE) final Cache<String, Set<AccountGroup.UUID>> membershipCache, @Named(LdapModule.USERNAME_CACHE) final Cache<String, Account.Id> usernameCache, @GerritServerConfig final Config config) { this.helper = helper; @@ -241,8 +241,8 @@ } @Override - public Set<AccountGroup.Id> groups(final AccountState who) { - final HashSet<AccountGroup.Id> r = new HashSet<AccountGroup.Id>(); + public Set<AccountGroup.UUID> groups(final AccountState who) { + final HashSet<AccountGroup.UUID> r = new HashSet<AccountGroup.UUID>(); r.addAll(membershipCache.get(findId(who.getExternalIds()))); r.addAll(who.getInternalGroups()); return r; @@ -324,7 +324,7 @@ } } - static class MemberLoader extends EntryCreator<String, Set<AccountGroup.Id>> { + static class MemberLoader extends EntryCreator<String, Set<AccountGroup.UUID>> { private final Helper helper; @Inject @@ -333,7 +333,7 @@ } @Override - public Set<AccountGroup.Id> createEntry(final String username) + public Set<AccountGroup.UUID> createEntry(final String username) throws Exception { final DirContext ctx = helper.open(); try { @@ -348,7 +348,7 @@ } @Override - public Set<AccountGroup.Id> missing(final String key) { + public Set<AccountGroup.UUID> missing(final String key) { return Collections.emptySet(); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ApprovalTypesProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ApprovalTypesProvider.java index db5bceb..0afaa05 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ApprovalTypesProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ApprovalTypesProvider.java
@@ -39,8 +39,7 @@ @Override public ApprovalTypes get() { - List<ApprovalType> approvalTypes = new ArrayList<ApprovalType>(2); - List<ApprovalType> actionTypes = new ArrayList<ApprovalType>(2); + List<ApprovalType> types = new ArrayList<ApprovalType>(2); try { final ReviewDb db = schema.open(); @@ -48,12 +47,7 @@ for (final ApprovalCategory c : db.approvalCategories().all()) { final List<ApprovalCategoryValue> values = db.approvalCategoryValues().byCategory(c.getId()).toList(); - final ApprovalType type = new ApprovalType(c, values); - if (type.getCategory().isAction()) { - actionTypes.add(type); - } else { - approvalTypes.add(type); - } + types.add(new ApprovalType(c, values)); } } finally { db.close(); @@ -62,8 +56,6 @@ throw new ProvisionException("Cannot query approval categories", e); } - approvalTypes = Collections.unmodifiableList(approvalTypes); - actionTypes = Collections.unmodifiableList(actionTypes); - return new ApprovalTypes(approvalTypes, actionTypes); + return new ApprovalTypes(Collections.unmodifiableList(types)); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java index 16a6a7c..a747ea4 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/AuthConfig.java
@@ -29,9 +29,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; /** Authentication related settings from {@code gerrit.config}. */ @Singleton @@ -45,10 +43,8 @@ private final boolean cookieSecure; private final SignedToken emailReg; - private final AccountGroup.Id administratorGroup; - private final Set<AccountGroup.Id> anonymousGroups; - private final Set<AccountGroup.Id> registeredGroups; - private final AccountGroup.Id batchUsersGroup; + private final AccountGroup.UUID administratorGroup; + private final AccountGroup.UUID batchUsersGroup; private final boolean allowGoogleAccountUpgrade; @@ -64,13 +60,8 @@ cookieSecure = cfg.getBoolean("auth", "cookiesecure", false); emailReg = new SignedToken(5 * 24 * 60 * 60, s.registerEmailPrivateKey); - final HashSet<AccountGroup.Id> r = new HashSet<AccountGroup.Id>(2); - r.add(s.anonymousGroupId); - r.add(s.registeredGroupId); - registeredGroups = Collections.unmodifiableSet(r); - anonymousGroups = Collections.singleton(s.anonymousGroupId); - administratorGroup = s.adminGroupId; - batchUsersGroup = s.batchUsersGroupId; + administratorGroup = s.adminGroupUUID; + batchUsersGroup = s.batchUsersGroupUUID; if (authType == AuthType.OPENID) { allowGoogleAccountUpgrade = @@ -127,25 +118,15 @@ } /** Identity of the magic group with full powers. */ - public AccountGroup.Id getAdministratorsGroup() { + public AccountGroup.UUID getAdministratorsGroup() { return administratorGroup; } /** Identity of the group whose service is degraded to lower priority. */ - public AccountGroup.Id getBatchUsersGroup() { + public AccountGroup.UUID getBatchUsersGroup() { return batchUsersGroup; } - /** Groups that all users, including anonymous users, belong to. */ - public Set<AccountGroup.Id> getAnonymousGroups() { - return anonymousGroups; - } - - /** Groups that all users who have created an account belong to. */ - public Set<AccountGroup.Id> getRegisteredGroups() { - return registeredGroups; - } - /** OpenID identities which the server permits for authentication. */ public List<OpenIdProviderPattern> getAllowedOpenIDs() { return allowedOpenIDs;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java index f24dcc5..3c102ba 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ConfigUtil.java
@@ -310,10 +310,10 @@ * @return the actual groups resolved from the database. If no groups are * found, returns an empty {@code Set}, never {@code null}. */ - public static Set<AccountGroup.Id> groupsFor( + public static Set<AccountGroup.UUID> groupsFor( SchemaFactory<ReviewDb> dbfactory, String[] groupNames, Logger log, String groupNotFoundWarning) { - final Set<AccountGroup.Id> result = new HashSet<AccountGroup.Id>(); + final Set<AccountGroup.UUID> result = new HashSet<AccountGroup.UUID>(); try { final ReviewDb db = dbfactory.open(); try { @@ -322,9 +322,16 @@ db.accountGroupNames().get(new AccountGroup.NameKey(name)); if (group == null) { log.warn(MessageFormat.format(groupNotFoundWarning, name)); - } else { - result.add(group.getId()); + continue; } + + AccountGroup ag = db.accountGroups().get(group.getId()); + if (ag == null) { + log.warn(MessageFormat.format(groupNotFoundWarning, name)); + continue; + } + + result.add(ag.getGroupUUID()); } } finally { db.close(); @@ -345,7 +352,7 @@ * @return the actual groups resolved from the database. If no groups are * found, returns an empty {@code Set}, never {@code null}. */ - public static Set<AccountGroup.Id> groupsFor( + public static Set<AccountGroup.UUID> groupsFor( SchemaFactory<ReviewDb> dbfactory, String[] groupNames, Logger log) { return groupsFor(dbfactory, groupNames, log, "Group \"{0}\" not in database, skipping.");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java index 5426f34..3f400a6 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritGlobalModule.java
@@ -23,8 +23,6 @@ import com.google.gerrit.reviewdb.Project; import com.google.gerrit.server.AnonymousUser; import com.google.gerrit.server.FileTypeRegistry; -import com.google.gerrit.server.GerritPersonIdent; -import com.google.gerrit.server.GerritPersonIdentProvider; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.MimeUtilFileTypeRegistry; import com.google.gerrit.server.ReplicationUser; @@ -41,8 +39,7 @@ import com.google.gerrit.server.cache.CachePool; import com.google.gerrit.server.events.EventFactory; import com.google.gerrit.server.git.ChangeMergeQueue; -import com.google.gerrit.server.git.GitRepositoryManager; -import com.google.gerrit.server.git.LocalDiskRepositoryManager; +import com.google.gerrit.server.git.GitModule; import com.google.gerrit.server.git.MergeQueue; import com.google.gerrit.server.git.PushAllProjectsOp; import com.google.gerrit.server.git.PushReplication; @@ -71,7 +68,6 @@ import org.apache.velocity.app.Velocity; import org.apache.velocity.runtime.RuntimeConstants; import org.eclipse.jgit.lib.Config; -import org.eclipse.jgit.lib.PersonIdent; import java.util.Properties; @@ -143,9 +139,6 @@ SINGLETON); bind(AnonymousUser.class); - bind(PersonIdent.class).annotatedWith(GerritPersonIdent.class).toProvider( - GerritPersonIdentProvider.class); - bind(IdGenerator.class); bind(CachePool.class); install(AccountByEmailCacheImpl.module()); @@ -155,13 +148,13 @@ install(PatchListCacheImpl.module()); install(ProjectCacheImpl.module()); install(new AccessControlModule()); + install(new GitModule()); factory(AccountInfoCacheFactory.Factory.class); factory(GroupInfoCacheFactory.Factory.class); factory(ProjectState.Factory.class); factory(RefControl.Factory.class); - bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class); bind(FileTypeRegistry.class).to(MimeUtilFileTypeRegistry.class); bind(WorkQueue.class); bind(ToolsCatalog.class); @@ -189,7 +182,6 @@ install(new LifecycleModule() { @Override protected void configure() { - listener().to(LocalDiskRepositoryManager.Lifecycle.class); listener().to(CachePool.Lifecycle.class); listener().to(WorkQueue.Lifecycle.class); listener().to(VelocityLifecycle.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java index f6dad7d..52525e7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GerritRequestModule.java
@@ -24,6 +24,7 @@ import com.google.gerrit.server.account.PerformCreateGroup; import com.google.gerrit.server.git.CreateCodeReviewNotes; import com.google.gerrit.server.git.MergeOp; +import com.google.gerrit.server.git.MetaDataUpdate; import com.google.gerrit.server.git.ReceiveCommits; import com.google.gerrit.server.mail.AbandonedSender; import com.google.gerrit.server.mail.AddReviewerSender; @@ -49,6 +50,7 @@ bind(ReviewDb.class).toProvider(RequestScopedReviewDbProvider.class).in( RequestScoped.class); bind(IdentifiedUser.RequestFactory.class).in(SINGLETON); + bind(MetaDataUpdate.User.class).in(RequestScoped.class); bind(AccountResolver.class); bind(ChangeQueryRewriter.class);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java index 9af6d62..1c13b9e 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitReceivePackGroupsProvider.java
@@ -22,21 +22,17 @@ import org.eclipse.jgit.lib.Config; import java.util.Collections; -import java.util.HashSet; public class GitReceivePackGroupsProvider extends GroupSetProvider { @Inject public GitReceivePackGroupsProvider(@GerritServerConfig Config config, - AuthConfig authConfig, SchemaFactory<ReviewDb> db) { + SchemaFactory<ReviewDb> db) { super(config, db, "receive", null, "allowGroup"); // If no group was set, default to "registered users" // if (groupIds.isEmpty()) { - HashSet<AccountGroup.Id> all = new HashSet<AccountGroup.Id>(); - all.addAll(authConfig.getRegisteredGroups()); - all.removeAll(authConfig.getAnonymousGroups()); - groupIds = Collections.unmodifiableSet(all); + groupIds = Collections.singleton(AccountGroup.REGISTERED_USERS); } } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java index bfb09a5..65e6900 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GitUploadPackGroupsProvider.java
@@ -27,15 +27,15 @@ public class GitUploadPackGroupsProvider extends GroupSetProvider { @Inject public GitUploadPackGroupsProvider(@GerritServerConfig Config config, - AuthConfig authConfig, SchemaFactory<ReviewDb> db) { + SchemaFactory<ReviewDb> db) { super(config, db, "upload", null, "allowGroup"); // If no group was set, default to "registered users" and "anonymous" // if (groupIds.isEmpty()) { - HashSet<AccountGroup.Id> all = new HashSet<AccountGroup.Id>(); - all.addAll(authConfig.getRegisteredGroups()); - all.addAll(authConfig.getAnonymousGroups()); + HashSet<AccountGroup.UUID> all = new HashSet<AccountGroup.UUID>(); + all.add(AccountGroup.REGISTERED_USERS); + all.add(AccountGroup.ANONYMOUS_USERS); groupIds = Collections.unmodifiableSet(all); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java index 373fdb5..6ff977f 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/GroupSetProvider.java
@@ -30,11 +30,11 @@ import java.util.Set; public abstract class GroupSetProvider implements - Provider<Set<AccountGroup.Id>> { + Provider<Set<AccountGroup.UUID>> { private static final Logger log = LoggerFactory.getLogger(GroupSetProvider.class); - protected Set<AccountGroup.Id> groupIds; + protected Set<AccountGroup.UUID> groupIds; @Inject protected GroupSetProvider(@GerritServerConfig Config config, @@ -44,7 +44,7 @@ } @Override - public Set<AccountGroup.Id> get() { + public Set<AccountGroup.UUID> get() { return groupIds; } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectCreatorGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectCreatorGroupsProvider.java index 381c914..e58e8bf 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectCreatorGroupsProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectCreatorGroupsProvider.java
@@ -15,7 +15,6 @@ package com.google.gerrit.server.config; import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gerrit.reviewdb.SystemConfig; import com.google.gwtorm.client.SchemaFactory; import com.google.inject.Inject; @@ -37,11 +36,11 @@ public class ProjectCreatorGroupsProvider extends GroupSetProvider { @Inject public ProjectCreatorGroupsProvider(@GerritServerConfig final Config config, - final SystemConfig systemConfig, final SchemaFactory<ReviewDb> db) { + final AuthConfig authConfig, final SchemaFactory<ReviewDb> db) { super(config, db, "repository", "*", "createGroup"); if (groupIds.isEmpty()) { - groupIds = Collections.singleton(systemConfig.adminGroupId); + groupIds = Collections.singleton(authConfig.getAdministratorsGroup()); } } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java index c457d73..4d5ad0c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/config/ProjectOwnerGroupsProvider.java
@@ -37,7 +37,7 @@ public class ProjectOwnerGroupsProvider extends GroupSetProvider { @Inject public ProjectOwnerGroupsProvider( - @ProjectCreatorGroups final Set<AccountGroup.Id> creatorGroups, + @ProjectCreatorGroups final Set<AccountGroup.UUID> creatorGroups, @GerritServerConfig final Config config, final SchemaFactory<ReviewDb> db) { super(config, db, "repository", "*", "ownerGroup");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePathFromSystemConfigProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePathFromSystemConfigProvider.java deleted file mode 100644 index 5b5dcc1..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/config/SitePathFromSystemConfigProvider.java +++ /dev/null
@@ -1,37 +0,0 @@ -// Copyright (C) 2009 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.config; - -import com.google.gerrit.reviewdb.SystemConfig; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.io.File; - -/** Provides {@link java.io.File} annotated with {@link SitePath}. */ -public class SitePathFromSystemConfigProvider implements Provider<File> { - private final File path; - - @Inject - SitePathFromSystemConfigProvider(final SystemConfig config) { - final String p = config.sitePath; - path = new File(p != null && p.length() > 0 ? p : ".").getAbsoluteFile(); - } - - @Override - public File get() { - return path; - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java index 573e6bb..beeaa59 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/events/EventFactory.java
@@ -212,7 +212,7 @@ a.by = asAccountAttribute(approval.getAccountId()); a.grantedOn = approval.getGranted().getTime() / 1000L; - ApprovalType at = approvalTypes.getApprovalType(approval.getCategoryId()); + ApprovalType at = approvalTypes.byId(approval.getCategoryId()); if (at != null) { a.description = at.getCategory().getName(); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java index 976ec2e..fb88ad6 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/CreateCodeReviewNotes.java
@@ -16,6 +16,7 @@ import static com.google.gerrit.server.git.GitRepositoryManager.REFS_NOTES_REVIEW; +import com.google.gerrit.common.data.ApprovalType; import com.google.gerrit.common.data.ApprovalTypes; import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.Change; @@ -197,10 +198,13 @@ } else if (ApprovalCategory.SUBMIT.equals(a.getCategoryId())) { submit = a; } else { - formatter.appendApproval( - approvalTypes.getApprovalType(a.getCategoryId()).getCategory(), - a.getValue(), - accountCache.get(a.getAccountId()).getAccount()); + ApprovalType type = approvalTypes.byId(a.getCategoryId()); + if (type != null) { + formatter.appendApproval( + type.getCategory(), + a.getValue(), + accountCache.get(a.getAccountId()).getAccount()); + } } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_49.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java similarity index 63% copy from gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_49.java copy to gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java index 0977ee9..7947d30 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_49.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitModule.java
@@ -12,15 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -package com.google.gerrit.server.schema; +package com.google.gerrit.server.git; -import com.google.inject.Inject; -import com.google.inject.Provider; +import com.google.gerrit.server.config.FactoryModule; -public class Schema_49 extends SchemaVersion { - - @Inject - Schema_49(Provider<Schema_48> prior) { - super(prior); +/** Configures the Git support. */ +public class GitModule extends FactoryModule { + @Override + protected void configure() { + factory(RenameGroupOp.Factory.class); + factory(MetaDataUpdate.InternalFactory.class); + bind(MetaDataUpdate.Server.class); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitProjectImporter.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitProjectImporter.java deleted file mode 100644 index 3f2ff0d..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitProjectImporter.java +++ /dev/null
@@ -1,121 +0,0 @@ -// Copyright (C) 2009 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.git; - -import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gerrit.reviewdb.Project.SubmitType; -import com.google.gwtorm.client.OrmException; -import com.google.gwtorm.client.SchemaFactory; -import com.google.inject.Inject; - -import org.eclipse.jgit.lib.RepositoryCache.FileKey; -import org.eclipse.jgit.util.FS; - -import java.io.File; -import java.io.IOException; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -/** Imports all projects found within the repository manager. */ -public class GitProjectImporter { - public interface Messages { - void info(String msg); - void warning(String msg); - } - - private final LocalDiskRepositoryManager repositoryManager; - private final SchemaFactory<ReviewDb> schema; - private Messages messages; - - @Inject - GitProjectImporter(final LocalDiskRepositoryManager repositoryManager, - final SchemaFactory<ReviewDb> schema) { - this.repositoryManager = repositoryManager; - this.schema = schema; - } - - public void run(final Messages msg) throws OrmException, IOException { - messages = msg; - messages.info("Scanning " + repositoryManager.getBasePath()); - final ReviewDb db = schema.open(); - try { - final HashSet<String> have = new HashSet<String>(); - for (Project p : db.projects().all()) { - have.add(p.getName()); - } - importProjects(repositoryManager.getBasePath(), "", db, have); - } finally { - db.close(); - } - } - - private void importProjects(final File dir, final String prefix, - final ReviewDb db, final Set<String> have) throws OrmException, - IOException { - final File[] ls = dir.listFiles(); - if (ls == null) { - return; - } - - for (File f : ls) { - String name = f.getName(); - if (".".equals(name) || "..".equals(name)) { - continue; - } - - if (FileKey.isGitRepository(f, FS.DETECTED)) { - if (name.equals(".git")) { - if ("".equals(prefix)) { - // If the git base path is itself a git repository working - // directory, this is a bit nonsensical for Gerrit Code Review. - // Skip the path and do the next one. - messages.warning("Skipping " + f.getAbsolutePath()); - continue; - } - name = prefix.substring(0, prefix.length() - 1); - - } else if (name.endsWith(".git")) { - name = prefix + name.substring(0, name.length() - 4); - - } else { - name = prefix + name; - if (!have.contains(name)) { - messages.warning("Importing non-standard name '" + name + "'"); - } - } - - if (have.contains(name)) { - continue; - } - - final Project.NameKey nameKey = new Project.NameKey(name); - final Project p = new Project(nameKey); - - p.setDescription(repositoryManager.getProjectDescription(nameKey)); - p.setSubmitType(SubmitType.MERGE_IF_NECESSARY); - p.setUseContributorAgreements(false); - p.setUseSignedOffBy(false); - p.setUseContentMerge(false); - p.setRequireChangeID(false); - db.projects().insert(Collections.singleton(p)); - - } else if (f.isDirectory()) { - importProjects(f, prefix + f.getName() + "/", db, have); - } - } - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java index a19b722..3e3d929 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/GitRepositoryManager.java
@@ -21,7 +21,7 @@ import org.eclipse.jgit.lib.Repository; import java.io.IOException; - +import java.util.SortedSet; /** * Manages Git repositories for the Gerrit server process. @@ -37,6 +37,9 @@ /** Note tree listing commits we refuse {@code refs/meta/reject-commits} */ public static final String REF_REJECT_COMMITS = "refs/meta/reject-commits"; + /** Configuration settings for a project {@code refs/meta/config} */ + public static final String REF_CONFIG = "refs/meta/config"; + /** * Get (or open) a repository by name. * @@ -61,6 +64,9 @@ public abstract Repository createRepository(Project.NameKey name) throws RepositoryNotFoundException; + /** @return set of all known projects, sorted by natural NameKey order. */ + public abstract SortedSet<Project.NameKey> list(); + /** * Read the {@code GIT_DIR/description} file for gitweb. * <p>
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java index 7e98348..1d348f7 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/LocalDiskRepositoryManager.java
@@ -23,10 +23,12 @@ import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RepositoryCache; import org.eclipse.jgit.lib.RepositoryCache.FileKey; +import org.eclipse.jgit.lib.StoredConfig; import org.eclipse.jgit.storage.file.LockFile; import org.eclipse.jgit.storage.file.WindowCache; import org.eclipse.jgit.storage.file.WindowCacheConfig; @@ -39,6 +41,9 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.Collections; +import java.util.SortedSet; +import java.util.TreeSet; /** Manages Git repositories stored on the local filesystem. */ @Singleton @@ -129,10 +134,19 @@ } loc = FileKey.exact(new File(basePath, n), FS.DETECTED); } - return RepositoryCache.open(loc, false); + + Repository db = RepositoryCache.open(loc, false); + db.create(true /* bare */); + + StoredConfig config = db.getConfig(); + config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, + null, ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true); + config.save(); + + return db; } catch (IOException e1) { final RepositoryNotFoundException e2; - e2 = new RepositoryNotFoundException("Cannot open repository " + name); + e2 = new RepositoryNotFoundException("Cannot create repository " + name); e2.initCause(e1); throw e2; } @@ -142,41 +156,48 @@ throws RepositoryNotFoundException, IOException { final Repository e = openRepository(name); try { - final File d = new File(e.getDirectory(), "description"); - - String description; - try { - description = RawParseUtils.decode(IO.readFully(d)); - } catch (FileNotFoundException err) { - return null; - } - - if (description != null) { - description = description.trim(); - if (description.isEmpty()) { - description = null; - } - if (UNNAMED.equals(description)) { - description = null; - } - } - return description; + return getProjectDescription(e); } finally { e.close(); } } + private String getProjectDescription(final Repository e) throws IOException { + final File d = new File(e.getDirectory(), "description"); + + String description; + try { + description = RawParseUtils.decode(IO.readFully(d)); + } catch (FileNotFoundException err) { + return null; + } + + if (description != null) { + description = description.trim(); + if (description.isEmpty()) { + description = null; + } + if (UNNAMED.equals(description)) { + description = null; + } + } + return description; + } + public void setProjectDescription(final Project.NameKey name, final String description) { // Update git's description file, in case gitweb is being used // try { - final Repository e; - final LockFile f; - - e = openRepository(name); + final Repository e = openRepository(name); try { - f = new LockFile(new File(e.getDirectory(), "description"), FS.DETECTED); + final String old = getProjectDescription(e); + if ((old == null && description == null) + || (old != null && old.equals(description))) { + return; + } + + final LockFile f = new LockFile(new File(e.getDirectory(), "description"), FS.DETECTED); if (f.lock()) { String d = description; if (d != null) { @@ -216,4 +237,46 @@ return false; // is a reasonable name } + + @Override + public SortedSet<Project.NameKey> list() { + SortedSet<Project.NameKey> names = new TreeSet<Project.NameKey>(); + scanProjects(basePath, "", names); + return Collections.unmodifiableSortedSet(names); + } + + private void scanProjects(final File dir, final String prefix, + final SortedSet<Project.NameKey> names) { + final File[] ls = dir.listFiles(); + if (ls == null) { + return; + } + + for (File f : ls) { + String fileName = f.getName(); + if (FileKey.isGitRepository(f, FS.DETECTED)) { + String projectName; + if (fileName.equals(Constants.DOT_GIT)) { + projectName = prefix.substring(0, prefix.length() - 1); + + } else if (fileName.endsWith(Constants.DOT_GIT_EXT)) { + int newLen = fileName.length() - Constants.DOT_GIT_EXT.length(); + projectName = prefix + fileName.substring(0, newLen); + + } else { + projectName = prefix + fileName; + } + + Project.NameKey nameKey = new Project.NameKey(projectName); + if (isUnreasonableName(nameKey)) { + log.warn("Ignoring unreasonably named repository " + f.getAbsolutePath()); + } else { + names.add(nameKey); + } + + } else if (f.isDirectory()) { + scanProjects(f, prefix + f.getName() + "/", names); + } + } + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java index b66fcb59..c69119b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MergeOp.java
@@ -805,7 +805,7 @@ tag = "Tested-by"; } else { final ApprovalType at = - approvalTypes.getApprovalType(a.getCategoryId()); + approvalTypes.byId(a.getCategoryId()); if (at == null) { // A deprecated/deleted approval type, ignore it. continue; @@ -882,6 +882,17 @@ private void updateBranch() throws MergeException { if (mergeTip != null && (branchTip == null || branchTip != mergeTip)) { + if (GitRepositoryManager.REF_CONFIG.equals(branchUpdate.getName())) { + try { + ProjectConfig cfg = new ProjectConfig(destProject.getNameKey()); + cfg.load(db, mergeTip); + } catch (Exception e) { + throw new MergeException("Submit would store invalid" + + " project configuration " + mergeTip.name() + " for " + + destProject.getName(), e); + } + } + branchUpdate.setForceUpdate(false); branchUpdate.setNewObjectId(mergeTip); branchUpdate.setRefLogMessage("merged", true); @@ -889,6 +900,13 @@ switch (branchUpdate.update(rw)) { case NEW: case FAST_FORWARD: + if (GitRepositoryManager.REF_CONFIG.equals(branchUpdate.getName())) { + projectCache.evict(destProject); + ProjectState ps = projectCache.get(destProject.getNameKey()); + repoManager.setProjectDescription(destProject.getNameKey(), // + ps.getProject().getDescription()); + } + replication.scheduleUpdate(destBranch.getParentKey(), branchUpdate .getName());
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/MetaDataUpdate.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/MetaDataUpdate.java new file mode 100644 index 0000000..568c8cf9 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/MetaDataUpdate.java
@@ -0,0 +1,128 @@ +// Copyright (C) 2011 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.git; + +import com.google.gerrit.reviewdb.Project; +import com.google.gerrit.server.GerritPersonIdent; +import com.google.gerrit.server.IdentifiedUser; +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; + +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.lib.CommitBuilder; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.Repository; + +/** Helps with the updating of a {@link VersionedMetaData}. */ +public class MetaDataUpdate { + public static class User { + private final InternalFactory factory; + private final GitRepositoryManager mgr; + private final PersonIdent serverIdent; + private final PersonIdent userIdent; + + @Inject + User(InternalFactory factory, GitRepositoryManager mgr, + @GerritPersonIdent PersonIdent serverIdent, IdentifiedUser currentUser) { + this.factory = factory; + this.mgr = mgr; + this.serverIdent = serverIdent; + this.userIdent = currentUser.newCommitterIdent( // + serverIdent.getWhen(), // + serverIdent.getTimeZone()); + } + + public PersonIdent getUserPersonIdent() { + return userIdent; + } + + public MetaDataUpdate create(Project.NameKey name) + throws RepositoryNotFoundException { + MetaDataUpdate md = factory.create(name, mgr.openRepository(name)); + md.getCommitBuilder().setAuthor(userIdent); + md.getCommitBuilder().setCommitter(serverIdent); + return md; + } + } + + public static class Server { + private final InternalFactory factory; + private final GitRepositoryManager mgr; + private final PersonIdent serverIdent; + + @Inject + Server(InternalFactory factory, GitRepositoryManager mgr, + @GerritPersonIdent PersonIdent serverIdent) { + this.factory = factory; + this.mgr = mgr; + this.serverIdent = serverIdent; + } + + public MetaDataUpdate create(Project.NameKey name) + throws RepositoryNotFoundException { + MetaDataUpdate md = factory.create(name, mgr.openRepository(name)); + md.getCommitBuilder().setAuthor(serverIdent); + md.getCommitBuilder().setCommitter(serverIdent); + return md; + } + } + + interface InternalFactory { + MetaDataUpdate create(@Assisted Project.NameKey projectName, + @Assisted Repository db); + } + + private final ReplicationQueue replication; + private final Project.NameKey projectName; + private final Repository db; + private final CommitBuilder commit; + + @Inject + public MetaDataUpdate(ReplicationQueue replication, + @Assisted Project.NameKey projectName, @Assisted Repository db) { + this.replication = replication; + this.projectName = projectName; + this.db = db; + this.commit = new CommitBuilder(); + } + + /** Set the commit message used when committing the update. */ + public void setMessage(String message) { + getCommitBuilder().setMessage(message); + } + + /** Close the cached Repository handle. */ + public void close() { + getRepository().close(); + } + + Project.NameKey getProjectName() { + return projectName; + } + + Repository getRepository() { + return db; + } + + public CommitBuilder getCommitBuilder() { + return commit; + } + + void replicate(String ref) { + if (replication.isEnabled()) { + replication.scheduleUpdate(projectName, ref); + } + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/NoReplication.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/NoReplication.java new file mode 100644 index 0000000..ceb70d2 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/NoReplication.java
@@ -0,0 +1,37 @@ +// Copyright (C) 2011 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.git; + +import com.google.gerrit.reviewdb.Project; + +/** A disabled {@link ReplicationQueue}. */ +public final class NoReplication implements ReplicationQueue { + @Override + public boolean isEnabled() { + return false; + } + + @Override + public void scheduleUpdate(Project.NameKey project, String ref) { + } + + @Override + public void scheduleFullSync(Project.NameKey project, String urlMatch) { + } + + @Override + public void replicateNewProject(Project.NameKey project, String head) { + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java new file mode 100644 index 0000000..651fc6a --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ProjectConfig.java
@@ -0,0 +1,391 @@ +// Copyright (C) 2010 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.git; + +import static com.google.gerrit.common.data.AccessSection.isAccessSection; +import static com.google.gerrit.common.data.Permission.isPermission; + +import com.google.gerrit.common.data.AccessSection; +import com.google.gerrit.common.data.GroupReference; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.common.data.PermissionRule; +import com.google.gerrit.reviewdb.AccountGroup; +import com.google.gerrit.reviewdb.Project; +import com.google.gerrit.reviewdb.Project.SubmitType; +import com.google.gerrit.server.account.GroupCache; + +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lib.CommitBuilder; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.ObjectId; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class ProjectConfig extends VersionedMetaData { + private static final String PROJECT_CONFIG = "project.config"; + private static final String GROUP_LIST = "groups"; + + private static final String PROJECT = "project"; + private static final String KEY_DESCRIPTION = "description"; + + private static final String ACCESS = "access"; + private static final String KEY_INHERIT_FROM = "inheritFrom"; + private static final String KEY_GROUP_PERMISSIONS = "exclusiveGroupPermissions"; + + private static final String RECEIVE = "receive"; + private static final String KEY_REQUIRE_SIGNED_OFF_BY = "requireSignedOffBy"; + private static final String KEY_REQUIRE_CHANGE_ID = "requireChangeId"; + private static final String KEY_REQUIRE_CONTRIBUTOR_AGREEMENT = + "requireContributorAgreement"; + + private static final String SUBMIT = "submit"; + private static final String KEY_ACTION = "action"; + private static final String KEY_MERGE_CONTENT = "mergeContent"; + + private static final SubmitType defaultSubmitAction = + SubmitType.MERGE_IF_NECESSARY; + + private Project.NameKey projectName; + private Project project; + private Map<AccountGroup.UUID, GroupReference> groupsByUUID; + private Map<String, AccessSection> accessSections; + + public static ProjectConfig read(MetaDataUpdate update) throws IOException, + ConfigInvalidException { + ProjectConfig r = new ProjectConfig(update.getProjectName()); + r.load(update); + return r; + } + + public static ProjectConfig read(MetaDataUpdate update, ObjectId id) + throws IOException, ConfigInvalidException { + ProjectConfig r = new ProjectConfig(update.getProjectName()); + r.load(update, id); + return r; + } + + public ProjectConfig(Project.NameKey projectName) { + this.projectName = projectName; + } + + public Project getProject() { + return project; + } + + public AccessSection getAccessSection(String name) { + return getAccessSection(name, false); + } + + public AccessSection getAccessSection(String name, boolean create) { + AccessSection as = accessSections.get(name); + if (as == null && create) { + as = new AccessSection(name); + accessSections.put(name, as); + } + return as; + } + + public Collection<AccessSection> getAccessSections() { + return sort(accessSections.values()); + } + + public void remove(AccessSection section) { + if (section != null) { + accessSections.remove(section.getRefPattern()); + } + } + + public void replace(AccessSection section) { + for (Permission permission : section.getPermissions()) { + for (PermissionRule rule : permission.getRules()) { + rule.setGroup(resolve(rule.getGroup())); + } + } + + accessSections.put(section.getRefPattern(), section); + } + + public GroupReference resolve(AccountGroup group) { + return resolve(GroupReference.forGroup(group)); + } + + public GroupReference resolve(GroupReference group) { + if (group != null) { + GroupReference ref = groupsByUUID.get(group.getUUID()); + if (ref != null) { + return ref; + } + groupsByUUID.put(group.getUUID(), group); + } + return group; + } + + /** @return the group reference, if the group is used by at least one rule. */ + public GroupReference getGroup(AccountGroup.UUID uuid) { + return groupsByUUID.get(uuid); + } + + /** + * Check all GroupReferences use current group name, repairing stale ones. + * + * @param groupCache cache to use when looking up group information by UUID. + * @return true if one or more group names was stale. + */ + public boolean updateGroupNames(GroupCache groupCache) { + boolean dirty = false; + for (GroupReference ref : groupsByUUID.values()) { + AccountGroup g = groupCache.get(ref.getUUID()); + if (g != null && !g.getName().equals(ref.getName())) { + dirty = true; + ref.setName(g.getName()); + } + } + return dirty; + } + + @Override + protected String getRefName() { + return GitRepositoryManager.REF_CONFIG; + } + + @Override + protected void onLoad() throws IOException, ConfigInvalidException { + Map<String,GroupReference> groupsByName = readGroupList(); + + Config rc = readConfig(PROJECT_CONFIG); + project = new Project(projectName); + + Project p = project; + p.setDescription(rc.getString(PROJECT, null, KEY_DESCRIPTION)); + if (p.getDescription() == null) { + p.setDescription(""); + } + p.setParentName(rc.getString(ACCESS, null, KEY_INHERIT_FROM)); + + p.setUseContributorAgreements(rc.getBoolean(RECEIVE, KEY_REQUIRE_CONTRIBUTOR_AGREEMENT, false)); + p.setUseSignedOffBy(rc.getBoolean(RECEIVE, KEY_REQUIRE_SIGNED_OFF_BY, false)); + p.setRequireChangeID(rc.getBoolean(RECEIVE, KEY_REQUIRE_CHANGE_ID, false)); + + p.setSubmitType(rc.getEnum(SUBMIT, null, KEY_ACTION, defaultSubmitAction)); + p.setUseContentMerge(rc.getBoolean(SUBMIT, null, KEY_MERGE_CONTENT, false)); + + accessSections = new HashMap<String, AccessSection>(); + for (String refName : rc.getSubsections(ACCESS)) { + if (isAccessSection(refName)) { + AccessSection as = getAccessSection(refName, true); + + for (String varName : rc.getStringList(ACCESS, refName, KEY_GROUP_PERMISSIONS)) { + for (String n : varName.split("[, \t]{1,}")) { + if (isPermission(n)) { + as.getPermission(n, true).setExclusiveGroup(true); + } + } + } + + for (String varName : rc.getNames(ACCESS, refName)) { + if (isPermission(varName)) { + Permission perm = as.getPermission(varName, true); + + boolean useRange = perm.isLabel(); + for (String ruleString : rc.getStringList(ACCESS, refName, varName)) { + PermissionRule rule; + try { + rule = PermissionRule.fromString(ruleString, useRange); + } catch (IllegalArgumentException notRule) { + throw new ConfigInvalidException("Invalid rule in " + ACCESS + + "." + refName + "." + varName + ": " + + notRule.getMessage(), notRule); + } + + GroupReference ref = groupsByName.get(rule.getGroup().getName()); + if (ref == null) { + // The group wasn't mentioned in the groups table, so there is + // no valid UUID for it. Pool the reference anyway so at least + // all rules in the same file share the same GroupReference. + // + ref = rule.getGroup(); + groupsByName.put(ref.getName(), ref); + } + + rule.setGroup(ref); + perm.add(rule); + } + } + } + } + } + } + + private Map<String, GroupReference> readGroupList() throws IOException, + ConfigInvalidException { + groupsByUUID = new HashMap<AccountGroup.UUID, GroupReference>(); + Map<String, GroupReference> groupsByName = + new HashMap<String, GroupReference>(); + + BufferedReader br = new BufferedReader(new StringReader(readUTF8(GROUP_LIST))); + String s; + while ((s = br.readLine()) != null) { + if (s.isEmpty() || s.startsWith("#")) { + continue; + } + + int tab = s.indexOf('\t'); + if (tab < 0) { + throw new ConfigInvalidException("Invalid group line: " + s); + } + + AccountGroup.UUID uuid = new AccountGroup.UUID(s.substring(0, tab).trim()); + String name = s.substring(tab + 1).trim(); + GroupReference ref = new GroupReference(uuid, name); + + groupsByUUID.put(uuid, ref); + groupsByName.put(name, ref); + } + return groupsByName; + } + + @Override + protected void onSave(CommitBuilder commit) throws IOException, + ConfigInvalidException { + if (commit.getMessage() == null || "".equals(commit.getMessage())) { + commit.setMessage("Updated project configuration\n"); + } + + Config rc = readConfig(PROJECT_CONFIG); + Project p = project; + + if (p.getDescription() != null && !p.getDescription().isEmpty()) { + rc.setString(PROJECT, null, KEY_DESCRIPTION, p.getDescription()); + } else { + rc.unset(PROJECT, null, KEY_DESCRIPTION); + } + set(rc, ACCESS, null, KEY_INHERIT_FROM, p.getParentName()); + + set(rc, RECEIVE, null, KEY_REQUIRE_CONTRIBUTOR_AGREEMENT, p.isUseContributorAgreements()); + set(rc, RECEIVE, null, KEY_REQUIRE_SIGNED_OFF_BY, p.isUseSignedOffBy()); + set(rc, RECEIVE, null, KEY_REQUIRE_CHANGE_ID, p.isRequireChangeID()); + + set(rc, SUBMIT, null, KEY_ACTION, p.getSubmitType(), defaultSubmitAction); + set(rc, SUBMIT, null, KEY_MERGE_CONTENT, p.isUseContentMerge()); + + Set<AccountGroup.UUID> keepGroups = new HashSet<AccountGroup.UUID>(); + for (AccessSection as : sort(accessSections.values())) { + String refName = as.getRefPattern(); + + StringBuilder doNotInherit = new StringBuilder(); + for (Permission perm : sort(as.getPermissions())) { + if (perm.getExclusiveGroup()) { + if (0 < doNotInherit.length()) { + doNotInherit.append(' '); + } + doNotInherit.append(perm.getName()); + } + } + if (0 < doNotInherit.length()) { + rc.setString(ACCESS, refName, KEY_GROUP_PERMISSIONS, doNotInherit.toString()); + } else { + rc.unset(ACCESS, refName, KEY_GROUP_PERMISSIONS); + } + + Set<String> have = new HashSet<String>(); + for (Permission permission : sort(as.getPermissions())) { + have.add(permission.getName().toLowerCase()); + + boolean needRange = permission.isLabel(); + List<String> rules = new ArrayList<String>(); + for (PermissionRule rule : sort(permission.getRules())) { + GroupReference group = rule.getGroup(); + if (group.getUUID() != null) { + keepGroups.add(group.getUUID()); + } + rules.add(rule.asString(needRange)); + } + rc.setStringList(ACCESS, refName, permission.getName(), rules); + } + + for (String varName : rc.getNames(ACCESS, refName)) { + if (isPermission(varName) && !have.contains(varName.toLowerCase())) { + rc.unset(ACCESS, refName, varName); + } + } + } + + for (String name : rc.getSubsections(ACCESS)) { + if (isAccessSection(name) && !accessSections.containsKey(name)) { + rc.unsetSection(ACCESS, name); + } + } + groupsByUUID.keySet().retainAll(keepGroups); + + saveConfig(PROJECT_CONFIG, rc); + saveGroupList(); + } + + private void saveGroupList() throws IOException { + if (groupsByUUID.isEmpty()) { + saveFile(GROUP_LIST, null); + return; + } + + final int uuidLen = 40; + StringBuilder buf = new StringBuilder(); + buf.append(pad(uuidLen, "# UUID")); + buf.append('\t'); + buf.append("Group Name"); + buf.append('\n'); + + buf.append('#'); + buf.append('\n'); + + for (GroupReference g : sort(groupsByUUID.values())) { + if (g.getUUID() != null && g.getName() != null) { + buf.append(pad(uuidLen, g.getUUID().get())); + buf.append('\t'); + buf.append(g.getName()); + buf.append('\n'); + } + } + saveUTF8(GROUP_LIST, buf.toString()); + } + + private static String pad(int len, String src) { + if (len <= src.length()) { + return src; + } + + StringBuilder r = new StringBuilder(len); + r.append(src); + while (r.length() < len) { + r.append(' '); + } + return r.toString(); + } + + private static <T extends Comparable<? super T>> List<T> sort(Collection<T> m) { + ArrayList<T> r = new ArrayList<T>(m); + Collections.sort(r); + return r; + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushAllProjectsOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushAllProjectsOp.java index 8985f29..b5f991c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushAllProjectsOp.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushAllProjectsOp.java
@@ -15,10 +15,7 @@ package com.google.gerrit.server.git; import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gerrit.server.config.WildProjectName; -import com.google.gwtorm.client.OrmException; -import com.google.gwtorm.client.SchemaFactory; +import com.google.gerrit.server.project.ProjectCache; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; @@ -37,20 +34,16 @@ private static final Logger log = LoggerFactory.getLogger(PushAllProjectsOp.class); - private final SchemaFactory<ReviewDb> schema; + private final ProjectCache projectCache; private final ReplicationQueue replication; - private final Project.NameKey wildProject; private final String urlMatch; @Inject - public PushAllProjectsOp(final WorkQueue wq, - final SchemaFactory<ReviewDb> sf, final ReplicationQueue rq, - @WildProjectName final Project.NameKey wp, - @Assisted @Nullable final String urlMatch) { + public PushAllProjectsOp(final WorkQueue wq, final ProjectCache projectCache, + final ReplicationQueue rq, @Assisted @Nullable final String urlMatch) { super(wq); - this.schema = sf; + this.projectCache = projectCache; this.replication = rq; - this.wildProject = wp; this.urlMatch = urlMatch; } @@ -63,17 +56,10 @@ public void run() { try { - final ReviewDb db = schema.open(); - try { - for (final Project project : db.projects().all()) { - if (!project.getNameKey().equals(wildProject)) { - replication.scheduleFullSync(project.getNameKey(), urlMatch); - } - } - } finally { - db.close(); + for (final Project.NameKey nameKey : projectCache.all()) { + replication.scheduleFullSync(nameKey, urlMatch); } - } catch (OrmException e) { + } catch (RuntimeException e) { log.error("Cannot enumerate known projects", e); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java index 9db9e44..295a143 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/PushReplication.java
@@ -354,7 +354,7 @@ String[] authGroupNames = cfg.getStringList("remote", rc.getName(), "authGroup"); - final Set<AccountGroup.Id> authGroups; + final Set<AccountGroup.UUID> authGroups; if (authGroupNames.length > 0) { authGroups = ConfigUtil.groupsFor(db, authGroupNames, // log, "Group \"{0}\" not in database, removing from authGroup");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java index 4380ff0..97ad546 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReceiveCommits.java
@@ -40,6 +40,7 @@ import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.account.AccountResolver; +import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.config.CanonicalWebUrl; import com.google.gerrit.server.config.TrackingFooters; import com.google.gerrit.server.mail.CreateChangeSender; @@ -48,7 +49,9 @@ import com.google.gerrit.server.mail.ReplacePatchSetSender; import com.google.gerrit.server.patch.PatchSetInfoFactory; import com.google.gerrit.server.project.ChangeControl; +import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectControl; +import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.server.project.RefControl; import com.google.gwtorm.client.AtomicUpdate; import com.google.gwtorm.client.OrmException; @@ -141,6 +144,9 @@ private final ReplicationQueue replication; private final PatchSetInfoFactory patchSetInfoFactory; private final ChangeHookRunner hooks; + private final GitRepositoryManager repoManager; + private final ProjectCache projectCache; + private final GroupCache groupCache; private final String canonicalWebUrl; private final PersonIdent gerritIdent; private final TrackingFooters trackingFooters; @@ -175,6 +181,9 @@ final ReplicationQueue replication, final PatchSetInfoFactory patchSetInfoFactory, final ChangeHookRunner hooks, + final ProjectCache projectCache, + final GitRepositoryManager repoManager, + final GroupCache groupCache, @CanonicalWebUrl @Nullable final String canonicalWebUrl, @GerritPersonIdent final PersonIdent gerritIdent, final TrackingFooters trackingFooters, @@ -191,6 +200,9 @@ this.replication = replication; this.patchSetInfoFactory = patchSetInfoFactory; this.hooks = hooks; + this.projectCache = projectCache; + this.repoManager = repoManager; + this.groupCache = groupCache; this.canonicalWebUrl = canonicalWebUrl; this.gerritIdent = gerritIdent; this.trackingFooters = trackingFooters; @@ -376,6 +388,13 @@ } } + if (isConfig(c)) { + projectCache.evict(project); + ProjectState ps = projectCache.get(project.getNameKey()); + repoManager.setProjectDescription(project.getNameKey(), // + ps.getProject().getDescription()); + } + if (!c.getRefName().startsWith(NEW_CHANGE)) { // We only schedule direct refs updates for replication. // Change refs are scheduled when they are created. @@ -402,9 +421,14 @@ AbstractAgreement bestAgreement = null; ContributorAgreement bestCla = null; - OUTER: for (AccountGroup.Id groupId : currentUser.getEffectiveGroups()) { + OUTER: for (AccountGroup.UUID groupUUID : currentUser.getEffectiveGroups()) { + AccountGroup group = groupCache.get(groupUUID); + if (group == null) { + continue; + } + final List<AccountGroupAgreement> temp = - db.accountGroupAgreements().byGroup(groupId).toList(); + db.accountGroupAgreements().byGroup(group.getId()).toList(); Collections.reverse(temp); @@ -555,24 +579,59 @@ switch (cmd.getType()) { case CREATE: parseCreate(cmd); - continue; + break; case UPDATE: parseUpdate(cmd); - continue; + break; case DELETE: parseDelete(cmd); - continue; + break; case UPDATE_NONFASTFORWARD: parseRewind(cmd); + break; + + default: + reject(cmd); continue; } - // Everything else is bogus as far as we are concerned. - // - reject(cmd); + if (cmd.getResult() != ReceiveCommand.Result.NOT_ATTEMPTED){ + continue; + } + + if (isConfig(cmd)) { + if (!projectControl.isOwner()) { + reject(cmd, "not project owner"); + continue; + } + + switch (cmd.getType()) { + case CREATE: + case UPDATE: + case UPDATE_NONFASTFORWARD: + try { + ProjectConfig cfg = new ProjectConfig(project.getNameKey()); + cfg.load(repo, cmd.getNewId()); + } catch (Exception e) { + reject(cmd, "invalid project configuration"); + log.error("User " + currentUser.getUserName() + + " tried to push invalid project configuration " + + cmd.getNewId().name() + " for " + project.getName(), e); + continue; + } + break; + + case DELETE: + break; + + default: + reject(cmd); + continue; + } + } } } @@ -1299,15 +1358,18 @@ oldCC.add(a.getAccountId()); } - final ApprovalType type = - approvalTypes.getApprovalType(a.getCategoryId()); - if (a.getPatchSetId().equals(priorPatchSet) - && type.getCategory().isCopyMinScore() && type.isMaxNegative(a)) { - // If there was a negative vote on the prior patch set, carry it - // into this patch set. - // - db.patchSetApprovals().insert( - Collections.singleton(new PatchSetApproval(ps.getId(), a))); + // ApprovalCategory.SUBMIT is still in db but not relevant in git-store + if (!ApprovalCategory.SUBMIT.equals(a.getCategoryId())) { + final ApprovalType type = + approvalTypes.byId(a.getCategoryId()); + if (a.getPatchSetId().equals(priorPatchSet) + && type.getCategory().isCopyMinScore() && type.isMaxNegative(a)) { + // If there was a negative vote on the prior patch set, carry it + // into this patch set. + // + db.patchSetApprovals().insert( + Collections.singleton(new PatchSetApproval(ps.getId(), a))); + } } if (!haveAuthor && authorId != null && a.getAccountId().equals(authorId)) { @@ -1929,4 +1991,9 @@ private static boolean isHead(final ReceiveCommand cmd) { return cmd.getRefName().startsWith(Constants.R_HEADS); } + + private static boolean isConfig(final ReceiveCommand cmd) { + return cmd.getRefName().equals(GitRepositoryManager.REF_CONFIG); + } + }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/RenameGroupOp.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/RenameGroupOp.java new file mode 100644 index 0000000..cedcb0b --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/RenameGroupOp.java
@@ -0,0 +1,153 @@ +// Copyright (C) 2011 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.git; + +import com.google.gerrit.common.data.GroupReference; +import com.google.gerrit.reviewdb.AccountGroup; +import com.google.gerrit.reviewdb.Project; +import com.google.gerrit.reviewdb.Project.NameKey; +import com.google.gerrit.server.project.ProjectCache; +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; + +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.lib.PersonIdent; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +public class RenameGroupOp extends DefaultQueueOp { + public interface Factory { + RenameGroupOp create(@Assisted("author") PersonIdent author, + @Assisted AccountGroup.UUID uuid, @Assisted("oldName") String oldName, + @Assisted("newName") String newName); + } + + private static final int MAX_TRIES = 10; + private static final Logger log = + LoggerFactory.getLogger(RenameGroupOp.class); + + private final ProjectCache projectCache; + private final MetaDataUpdate.Server metaDataUpdateFactory; + + private final PersonIdent author; + private final AccountGroup.UUID uuid; + private final String oldName; + private final String newName; + private final List<Project.NameKey> retryOn; + + private boolean tryingAgain; + + @Inject + public RenameGroupOp(WorkQueue workQueue, ProjectCache projectCache, + MetaDataUpdate.Server metaDataUpdateFactory, + + @Assisted("author") PersonIdent author, @Assisted AccountGroup.UUID uuid, + @Assisted("oldName") String oldName, @Assisted("newName") String newName) { + super(workQueue); + this.projectCache = projectCache; + this.metaDataUpdateFactory = metaDataUpdateFactory; + + this.author = author; + this.uuid = uuid; + this.oldName = oldName; + this.newName = newName; + this.retryOn = new ArrayList<Project.NameKey>(); + } + + @Override + public void run() { + Iterable<NameKey> names = tryingAgain ? retryOn : projectCache.all(); + for (Project.NameKey projectName : names) { + ProjectConfig config = projectCache.get(projectName).getConfig(); + GroupReference ref = config.getGroup(uuid); + if (ref == null || newName.equals(ref.getName())) { + continue; + } + + try { + MetaDataUpdate md = metaDataUpdateFactory.create(projectName); + try { + rename(md); + } finally { + md.close(); + } + } catch (RepositoryNotFoundException noProject) { + continue; + } catch (ConfigInvalidException err) { + log.error("Cannot rename group " + oldName + " in " + projectName, err); + } catch (IOException err) { + log.error("Cannot rename group " + oldName + " in " + projectName, err); + } + } + + // If one or more projects did not update, wait 5 minutes + // and give it another attempt. + if (!retryOn.isEmpty() && !tryingAgain) { + tryingAgain = true; + start(5, TimeUnit.MINUTES); + } + } + + private void rename(MetaDataUpdate md) throws IOException, + ConfigInvalidException { + boolean success = false; + for (int attempts = 0; !success && attempts < MAX_TRIES; attempts++) { + ProjectConfig config = ProjectConfig.read(md); + + // The group isn't referenced, or its name has been fixed already. + // + GroupReference ref = config.getGroup(uuid); + if (ref == null || newName.equals(ref.getName())) { + projectCache.evict(config.getProject()); + return; + } + + ref.setName(newName); + md.getCommitBuilder().setAuthor(author); + md.setMessage("Rename group " + oldName + " to " + newName + "\n"); + if (config.commit(md)) { + projectCache.evict(config.getProject()); + success = true; + + } else { + try { + Thread.sleep(25 /* milliseconds */); + } catch (InterruptedException wakeUp) { + continue; + } + } + } + + if (!success) { + if (tryingAgain) { + log.warn("Could not rename group " + oldName + " to " + newName + + " in " + md.getProjectName().get()); + } else { + retryOn.add(md.getProjectName()); + } + } + } + + @Override + public String toString() { + return "Rename Group " + oldName; + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReviewNoteHeaderFormatter.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReviewNoteHeaderFormatter.java index d54dcec..8997e2b 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/git/ReviewNoteHeaderFormatter.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/ReviewNoteHeaderFormatter.java
@@ -51,8 +51,7 @@ void appendApproval(ApprovalCategory category, short value, Account user) { - // TODO: use category.getLabel() when available - sb.append(category.getName().replace(' ', '-')); + sb.append(category.getLabelName()); sb.append(value < 0 ? "-" : "+").append(Math.abs(value)).append(": "); appendUserData(user); sb.append("\n");
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java new file mode 100644 index 0000000..bb91044 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/VersionedMetaData.java
@@ -0,0 +1,318 @@ +// Copyright (C) 2010 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.git; + +import org.eclipse.jgit.dircache.DirCache; +import org.eclipse.jgit.dircache.DirCacheBuilder; +import org.eclipse.jgit.dircache.DirCacheEditor; +import org.eclipse.jgit.dircache.DirCacheEntry; +import org.eclipse.jgit.dircache.DirCacheEditor.DeletePath; +import org.eclipse.jgit.dircache.DirCacheEditor.PathEdit; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.errors.UnmergedPathException; +import org.eclipse.jgit.lib.CommitBuilder; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.Constants; +import org.eclipse.jgit.lib.FileMode; +import org.eclipse.jgit.lib.ObjectId; +import org.eclipse.jgit.lib.ObjectInserter; +import org.eclipse.jgit.lib.ObjectLoader; +import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevTree; +import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.treewalk.TreeWalk; +import org.eclipse.jgit.util.RawParseUtils; + +import java.io.IOException; + +/** + * Support for metadata stored within a version controlled branch. + * <p> + * Implementors are responsible for supplying implementations of the onLoad and + * onSave methods to read from the repository, or format an update that can + * later be written back to the repository. + */ +public abstract class VersionedMetaData { + private RevCommit revision; + private ObjectReader reader; + private ObjectInserter inserter; + private DirCache newTree; + + /** @return name of the reference storing this configuration. */ + protected abstract String getRefName(); + + protected abstract void onLoad() throws IOException, ConfigInvalidException; + + protected abstract void onSave(CommitBuilder commit) throws IOException, + ConfigInvalidException; + + /** @return revision of the metadata that was loaded. */ + public ObjectId getRevision() { + return revision.copy(); + } + + /** Initialize in-memory as though the repository branch doesn't exist. */ + public void createInMemory() { + try { + revision = null; + onLoad(); + } catch (IOException err) { + throw new RuntimeException("Unexpected IOException", err); + } catch (ConfigInvalidException err) { + throw new RuntimeException("Unexpected ConfigInvalidException", err); + } + } + + /** + * Load the current version from the branch. + * <p> + * The repository is not held after the call completes, allowing the + * application to retain this object for long periods of time. + * + * @param db repository to access. + * @throws IOException + * @throws ConfigInvalidException + */ + public void load(Repository db) throws IOException, ConfigInvalidException { + Ref ref = db.getRef(getRefName()); + load(db, ref != null ? ref.getObjectId() : null); + } + + /** + * Load a specific version from the repository. + * <p> + * This method is primarily useful for applying updates to a specific revision + * that was shown to an end-user in the user interface. If there are conflicts + * with another user's concurrent changes, these will be automatically + * detected at commit time. + * <p> + * The repository is not held after the call completes, allowing the + * application to retain this object for long periods of time. + * + * @param db repository to access. + * @param id revision to load. + * @throws IOException + * @throws ConfigInvalidException + */ + public void load(Repository db, ObjectId id) throws IOException, + ConfigInvalidException { + if (id != null) { + reader = db.newObjectReader(); + try { + revision = new RevWalk(reader).parseCommit(id); + onLoad(); + } finally { + reader.release(); + reader = null; + } + } else { + // The branch does not yet exist. + revision = null; + onLoad(); + } + } + + public void load(MetaDataUpdate update) throws IOException, + ConfigInvalidException { + load(update.getRepository()); + } + + public void load(MetaDataUpdate update, ObjectId id) throws IOException, + ConfigInvalidException { + load(update.getRepository(), id); + } + + /** + * Update this metadata branch, recording a new commit on its reference. + * + * @param update helper information to define the update that will occur. + * @return true if the update was successful, false if it failed because of a + * concurrent update to the same reference. + * @throws IOException if there is a storage problem and the update cannot be + * executed as requested. + */ + public boolean commit(MetaDataUpdate update) throws IOException { + final Repository db = update.getRepository(); + final CommitBuilder commit = update.getCommitBuilder(); + + reader = db.newObjectReader(); + inserter = db.newObjectInserter(); + try { + final RevWalk rw = new RevWalk(reader); + final RevTree src = revision != null ? rw.parseTree(revision) : null; + final ObjectId res = writeTree(src, commit); + + if (res.equals(src)) { + // If there are no changes to the content, don't create the commit. + return true; + } + + commit.setTreeId(res); + if (revision != null) { + commit.setParentId(revision); + } + + RefUpdate ru = db.updateRef(getRefName()); + if (revision != null) { + ru.setExpectedOldObjectId(revision); + } else { + ru.setExpectedOldObjectId(ObjectId.zeroId()); + } + ru.setNewObjectId(inserter.insert(commit)); + ru.disableRefLog(); + inserter.flush(); + + switch (ru.update(rw)) { + case NEW: + case FAST_FORWARD: + revision = rw.parseCommit(ru.getNewObjectId()); + update.replicate(ru.getName()); + return true; + + case LOCK_FAILURE: + return false; + + default: + throw new IOException("Cannot update " + ru.getName() + " in " + + db.getDirectory() + ": " + ru.getResult()); + } + } catch (ConfigInvalidException e) { + throw new IOException("Cannot update " + getRefName() + " in " + + db.getDirectory() + ": " + e.getMessage(), e); + } finally { + inserter.release(); + inserter = null; + + reader.release(); + reader = null; + } + } + + private ObjectId writeTree(RevTree srcTree, CommitBuilder commit) + throws IOException, MissingObjectException, IncorrectObjectTypeException, + UnmergedPathException, ConfigInvalidException { + try { + newTree = readTree(srcTree); + onSave(commit); + return newTree.writeTree(inserter); + } finally { + newTree = null; + } + } + + private DirCache readTree(RevTree tree) throws IOException, + MissingObjectException, IncorrectObjectTypeException { + DirCache dc = DirCache.newInCore(); + if (tree != null) { + DirCacheBuilder b = dc.builder(); + b.addTree(new byte[0], DirCacheEntry.STAGE_0, reader, tree); + b.finish(); + } + return dc; + } + + protected Config readConfig(String fileName) throws IOException, + ConfigInvalidException { + Config rc = new Config(); + String text = readUTF8(fileName); + if (!text.isEmpty()) { + try { + rc.fromText(text); + } catch (ConfigInvalidException err) { + throw new ConfigInvalidException("Invalid config file " + fileName + + " in commit" + revision.name(), err); + } + } + return rc; + } + + protected String readUTF8(String fileName) throws IOException { + byte[] raw = readFile(fileName); + return raw.length != 0 ? RawParseUtils.decode(raw) : ""; + } + + protected byte[] readFile(String fileName) throws IOException { + if (revision == null) { + return new byte[] {}; + } + + TreeWalk tw = TreeWalk.forPath(reader, fileName, revision.getTree()); + if (tw != null) { + ObjectLoader obj = reader.open(tw.getObjectId(0), Constants.OBJ_BLOB); + return obj.getCachedBytes(Integer.MAX_VALUE); + + } else { + return new byte[] {}; + } + } + + protected static void set(Config rc, String section, String subsection, + String name, String value) { + if (value != null) { + rc.setString(section, subsection, name, value); + } else { + rc.unset(section, subsection, name); + } + } + + protected static void set(Config rc, String section, String subsection, + String name, boolean value) { + if (value) { + rc.setBoolean(section, subsection, name, value); + } else { + rc.unset(section, subsection, name); + } + } + + protected static <E extends Enum<?>> void set(Config rc, String section, + String subsection, String name, E value, E defaultValue) { + if (value != defaultValue) { + rc.setEnum(section, subsection, name, value); + } else { + rc.unset(section, subsection, name); + } + } + + protected void saveConfig(String fileName, Config cfg) throws IOException { + saveUTF8(fileName, cfg.toText()); + } + + protected void saveUTF8(String fileName, String text) throws IOException { + saveFile(fileName, text != null ? Constants.encode(text) : null); + } + + protected void saveFile(String fileName, byte[] raw) throws IOException { + DirCacheEditor editor = newTree.editor(); + if (raw != null && 0 < raw.length) { + final ObjectId blobId = inserter.insert(Constants.OBJ_BLOB, raw); + editor.add(new PathEdit(fileName) { + @Override + public void apply(DirCacheEntry ent) { + ent.setFileMode(FileMode.REGULAR_FILE); + ent.setObjectId(blobId); + } + }); + } else { + editor.add(new DeletePath(fileName)); + } + editor.finish(); + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java index f5a7870..ebbe686 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/ChangeEmail.java
@@ -69,8 +69,8 @@ /** Is the from user in an email squelching group? */ final IdentifiedUser user = args.identifiedUserFactory.create(id); - final Set<AccountGroup.Id> gids = user.getEffectiveGroups(); - for (final AccountGroup.Id gid : gids) { + final Set<AccountGroup.UUID> gids = user.getEffectiveGroups(); + for (final AccountGroup.UUID gid : gids) { if (args.groupCache.get(gid).isEmailOnlyAuthors()) { emailOnlyAuthors = true; break; @@ -273,11 +273,11 @@ } /** Get the groups which own the project. */ - protected Set<AccountGroup.Id> getProjectOwners() { + protected Set<AccountGroup.UUID> getProjectOwners() { final ProjectState r; r = args.projectCache.get(change.getProject()); - return r != null ? r.getOwners() : Collections.<AccountGroup.Id> emptySet(); + return r != null ? r.getOwners() : Collections.<AccountGroup.UUID> emptySet(); } /** TO or CC all vested parties (change owner, patch set uploader, author). */
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java index c14ff1b..8bb68df 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/mail/CreateChangeSender.java
@@ -20,6 +20,7 @@ import com.google.gerrit.reviewdb.AccountProjectWatch; import com.google.gerrit.reviewdb.Change; import com.google.gerrit.reviewdb.AccountProjectWatch.NotifyType; +import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.ssh.SshInfo; import com.google.gwtorm.client.OrmException; import com.google.inject.Inject; @@ -34,10 +35,13 @@ public CreateChangeSender create(Change change); } + private final GroupCache groupCache; + @Inject public CreateChangeSender(EmailArguments ea, SshInfo sshInfo, - @Assisted Change c) { + GroupCache groupCache, @Assisted Change c) { super(ea, sshInfo, c); + this.groupCache = groupCache; } @Override @@ -52,10 +56,13 @@ // Try to mark interested owners with a TO and not a BCC line. // final Set<Account.Id> owners = new HashSet<Account.Id>(); - for (AccountGroup.Id g : getProjectOwners()) { - for (AccountGroupMember m : args.db.get().accountGroupMembers() - .byGroup(g)) { - owners.add(m.getAccountId()); + for (AccountGroup.UUID uuid : getProjectOwners()) { + AccountGroup group = groupCache.get(uuid); + if (group != null) { + for (AccountGroupMember m : args.db.get().accountGroupMembers() + .byGroup(group.getId())) { + owners.add(m.getAccountId()); + } } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PublishComments.java b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PublishComments.java index f3e2890..db297ae 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/patch/PublishComments.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/patch/PublishComments.java
@@ -171,7 +171,9 @@ final short o = a.getValue(); a.setValue(want.get()); a.cache(change); - functionState.normalize(types.getApprovalType(a.getCategoryId()), a); + if (!ApprovalCategory.SUBMIT.equals(a.getCategoryId())) { + functionState.normalize(types.byId(a.getCategoryId()), a); + } if (o != a.getValue()) { // Value changed, ensure we update the database. //
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/AccessControlModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/AccessControlModule.java index 1e2e7f4..64e5299 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/AccessControlModule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/AccessControlModule.java
@@ -33,19 +33,19 @@ public class AccessControlModule extends FactoryModule { @Override protected void configure() { - bind(new TypeLiteral<Set<AccountGroup.Id>>() {}) // + bind(new TypeLiteral<Set<AccountGroup.UUID>>() {}) // .annotatedWith(ProjectCreatorGroups.class) // .toProvider(ProjectCreatorGroupsProvider.class).in(SINGLETON); - bind(new TypeLiteral<Set<AccountGroup.Id>>() {}) // + bind(new TypeLiteral<Set<AccountGroup.UUID>>() {}) // .annotatedWith(ProjectOwnerGroups.class) // .toProvider(ProjectOwnerGroupsProvider.class).in(SINGLETON); - bind(new TypeLiteral<Set<AccountGroup.Id>>() {}) // + bind(new TypeLiteral<Set<AccountGroup.UUID>>() {}) // .annotatedWith(GitUploadPackGroups.class) // .toProvider(GitUploadPackGroupsProvider.class).in(SINGLETON); - bind(new TypeLiteral<Set<AccountGroup.Id>>() {}) // + bind(new TypeLiteral<Set<AccountGroup.UUID>>() {}) // .annotatedWith(GitReceivePackGroups.class) // .toProvider(GitReceivePackGroupsProvider.class).in(SINGLETON);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java index 3291f1a..b387e70 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ChangeControl.java
@@ -16,13 +16,12 @@ import com.google.gerrit.common.data.ApprovalType; import com.google.gerrit.common.data.ApprovalTypes; -import com.google.gerrit.reviewdb.ApprovalCategory; +import com.google.gerrit.common.data.PermissionRange; import com.google.gerrit.reviewdb.Change; import com.google.gerrit.reviewdb.PatchSet; import com.google.gerrit.reviewdb.PatchSetApproval; import com.google.gerrit.reviewdb.Project; import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gerrit.server.ChangeUtil; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.IdentifiedUser; import com.google.gerrit.server.workflow.CategoryFunction; @@ -31,7 +30,6 @@ import com.google.inject.Inject; import com.google.inject.Provider; -import java.util.ArrayList; import java.util.List; @@ -164,8 +162,14 @@ return canAbandon(); // Anyone who can abandon the change can restore it back } - public short normalize(ApprovalCategory.Id category, short score) { - return getRefControl().normalize(category, score); + /** All value ranges of any allowed label permission. */ + public List<PermissionRange> getLabelRanges() { + return getRefControl().getLabelRanges(); + } + + /** The range of permitted values associated with a label permission. */ + public PermissionRange getRange(String permission) { + return getRefControl().getRange(permission); } /** Can this user add a patch set to this change? */ @@ -240,34 +244,22 @@ return result; } - final List<PatchSetApproval> allApprovals = - new ArrayList<PatchSetApproval>(db.patchSetApprovals().byPatchSet( - patchSetId).toList()); - final PatchSetApproval myAction = - ChangeUtil.createSubmitApproval(patchSetId, - (IdentifiedUser) getCurrentUser(), db); - - final ApprovalType actionType = - approvalTypes.getApprovalType(myAction.getCategoryId()); - if (actionType == null || !actionType.getCategory().isAction()) { - return new CanSubmitResult("Invalid action " + myAction.getCategoryId()); - } + final List<PatchSetApproval> all = + db.patchSetApprovals().byPatchSet(patchSetId).toList(); final FunctionState fs = - functionStateFactory.create(change, patchSetId, allApprovals); + functionStateFactory.create(change, patchSetId, all); + for (ApprovalType c : approvalTypes.getApprovalTypes()) { CategoryFunction.forCategory(c.getCategory()).run(c, fs); } - if (!CategoryFunction.forCategory(actionType.getCategory()).isValid( - getCurrentUser(), actionType, fs)) { - return new CanSubmitResult(actionType.getCategory().getName() - + " not permitted"); + + for (ApprovalType type : approvalTypes.getApprovalTypes()) { + if (!fs.isValid(type)) { + return new CanSubmitResult("Requires " + type.getCategory().getName()); + } } - fs.normalize(actionType, myAction); - if (myAction.getValue() <= 0) { - return new CanSubmitResult(actionType.getCategory().getName() - + " not permitted"); - } + return CanSubmitResult.OK; } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java index 35b5ee5..4202a62 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCache.java
@@ -29,6 +29,17 @@ /** Invalidate the cached information about the given project. */ public void evict(Project p); - /** Invalidate the cached information about all projects. */ - public void evictAll(); + /** @return sorted iteration of projects. */ + public abstract Iterable<Project.NameKey> all(); + + /** + * Filter the set of registered project names by common prefix. + * + * @param prefix common prefix. + * @return sorted iteration of projects sharing the same prefix. + */ + public abstract Iterable<Project.NameKey> byName(String prefix); + + /** Notify the cache that a new project was constructed. */ + public void onCreateProject(Project.NameKey newProjectName); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java index 48eef87..6459e3c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
@@ -15,11 +15,12 @@ package com.google.gerrit.server.project; import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.RefRight; import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.server.cache.Cache; import com.google.gerrit.server.cache.CacheModule; import com.google.gerrit.server.cache.EntryCreator; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.git.ProjectConfig; import com.google.gwtorm.client.SchemaFactory; import com.google.inject.Inject; import com.google.inject.Module; @@ -27,21 +28,35 @@ import com.google.inject.TypeLiteral; import com.google.inject.name.Named; -import java.util.Collection; +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.lib.Repository; + import java.util.Collections; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.SortedSet; +import java.util.TreeSet; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /** Cache of project information, including access rights. */ @Singleton public class ProjectCacheImpl implements ProjectCache { private static final String CACHE_NAME = "projects"; + private static final String CACHE_LIST = "project_list"; public static Module module() { return new CacheModule() { @Override protected void configure() { - final TypeLiteral<Cache<Project.NameKey, ProjectState>> type = + final TypeLiteral<Cache<Project.NameKey, ProjectState>> nameType = new TypeLiteral<Cache<Project.NameKey, ProjectState>>() {}; - core(type, CACHE_NAME).populateWith(Loader.class); + core(nameType, CACHE_NAME).populateWith(Loader.class); + + final TypeLiteral<Cache<ListKey, SortedSet<Project.NameKey>>> listType = + new TypeLiteral<Cache<ListKey, SortedSet<Project.NameKey>>>() {}; + core(listType, CACHE_LIST).populateWith(Lister.class); + bind(ProjectCacheImpl.class); bind(ProjectCache.class).to(ProjectCacheImpl.class); } @@ -49,11 +64,16 @@ } private final Cache<Project.NameKey, ProjectState> byName; + private final Cache<ListKey,SortedSet<Project.NameKey>> list; + private final Lock listLock; @Inject ProjectCacheImpl( - @Named(CACHE_NAME) final Cache<Project.NameKey, ProjectState> byName) { + @Named(CACHE_NAME) final Cache<Project.NameKey, ProjectState> byName, + @Named(CACHE_LIST) final Cache<ListKey, SortedSet<Project.NameKey>> list) { this.byName = byName; + this.list = list; + this.listLock = new ReentrantLock(true /* fair */); } /** @@ -73,38 +93,127 @@ } } - /** Invalidate the cached information about all projects. */ - public void evictAll() { - byName.removeAll(); + @Override + public void onCreateProject(Project.NameKey newProjectName) { + listLock.lock(); + try { + SortedSet<Project.NameKey> n = list.get(ListKey.ALL); + n = new TreeSet<Project.NameKey>(n); + n.add(newProjectName); + list.put(ListKey.ALL, Collections.unmodifiableSortedSet(n)); + } finally { + listLock.unlock(); + } + } + + @Override + public Iterable<Project.NameKey> all() { + return list.get(ListKey.ALL); + } + + @Override + public Iterable<Project.NameKey> byName(final String pfx) { + return new Iterable<Project.NameKey>() { + @Override + public Iterator<Project.NameKey> iterator() { + return new Iterator<Project.NameKey>() { + private Project.NameKey next; + private Iterator<Project.NameKey> itr = + list.get(ListKey.ALL).tailSet(new Project.NameKey(pfx)).iterator(); + + @Override + public boolean hasNext() { + if (next != null) { + return true; + } + + if (!itr.hasNext()) { + return false; + } + + Project.NameKey r = itr.next(); + if (r.get().startsWith(pfx)) { + next = r; + return true; + } else { + itr = Collections.<Project.NameKey> emptyList().iterator(); + return false; + } + } + + @Override + public Project.NameKey next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + + Project.NameKey r = next; + next = null; + return r; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; } static class Loader extends EntryCreator<Project.NameKey, ProjectState> { private final ProjectState.Factory projectStateFactory; private final SchemaFactory<ReviewDb> schema; + private final GitRepositoryManager mgr; @Inject - Loader(ProjectState.Factory psf, SchemaFactory<ReviewDb> sf) { + Loader(ProjectState.Factory psf, SchemaFactory<ReviewDb> sf, + GitRepositoryManager g) { projectStateFactory = psf; schema = sf; + mgr = g; } @Override public ProjectState createEntry(Project.NameKey key) throws Exception { final ReviewDb db = schema.open(); try { - final Project p = db.projects().get(key); - if (p == null) { - return null; + Repository git = mgr.openRepository(key); + try { + final ProjectConfig cfg = new ProjectConfig(key); + cfg.load(git); + return projectStateFactory.create(cfg); + } finally { + git.close(); } - final Collection<RefRight> rights = - Collections.unmodifiableCollection(db.refRights().byProject( - p.getNameKey()).toList()); + } catch (RepositoryNotFoundException notFound) { + return null; - return projectStateFactory.create(p, rights); } finally { db.close(); } } } + + static class ListKey { + static final ListKey ALL = new ListKey(); + + private ListKey() { + } + } + + static class Lister extends EntryCreator<ListKey, SortedSet<Project.NameKey>> { + private final GitRepositoryManager mgr; + + @Inject + Lister(GitRepositoryManager mgr) { + this.mgr = mgr; + } + + @Override + public SortedSet<Project.NameKey> createEntry(ListKey key) throws Exception { + return mgr.list(); + } + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java index 25778a6..4b11849 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectControl.java
@@ -14,13 +14,15 @@ package com.google.gerrit.server.project; -import static com.google.gerrit.common.CollectionsUtil.*; +import static com.google.gerrit.common.CollectionsUtil.isAnyIncludedIn; + +import com.google.gerrit.common.data.AccessSection; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.reviewdb.AccountGroup; -import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.Branch; import com.google.gerrit.reviewdb.Change; import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.RefRight; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.ReplicationUser; import com.google.gerrit.server.config.GitReceivePackGroups; @@ -29,6 +31,7 @@ import com.google.inject.Provider; import com.google.inject.assistedinject.Assisted; +import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -101,16 +104,18 @@ ProjectControl create(CurrentUser who, ProjectState ps); } - private final Set<AccountGroup.Id> uploadGroups; - private final Set<AccountGroup.Id> receiveGroups; + private final Set<AccountGroup.UUID> uploadGroups; + private final Set<AccountGroup.UUID> receiveGroups; private final RefControl.Factory refControlFactory; private final CurrentUser user; private final ProjectState state; + private Collection<AccessSection> access; + @Inject - ProjectControl(@GitUploadPackGroups Set<AccountGroup.Id> uploadGroups, - @GitReceivePackGroups Set<AccountGroup.Id> receiveGroups, + ProjectControl(@GitUploadPackGroups Set<AccountGroup.UUID> uploadGroups, + @GitReceivePackGroups Set<AccountGroup.UUID> receiveGroups, final RefControl.Factory refControlFactory, @Assisted CurrentUser who, @Assisted ProjectState ps) { this.uploadGroups = uploadGroups; @@ -155,18 +160,18 @@ /** Can this user see this project exists? */ public boolean isVisible() { return visibleForReplication() - || canPerformOnAnyRef(ApprovalCategory.READ, (short) 1); + || canPerformOnAnyRef(Permission.READ); } public boolean canAddRefs() { - return (canPerformOnAnyRef(ApprovalCategory.PUSH_HEAD, ApprovalCategory.PUSH_HEAD_CREATE) + return (canPerformOnAnyRef(Permission.CREATE) || isOwnerAnyRef()); } /** Can this user see all the refs in this projects? */ public boolean allRefsAreVisible() { return visibleForReplication() - || canPerformOnAllRefs(ApprovalCategory.READ, (short) 1); + || canPerformOnAllRefs(Permission.READ); } /** Is this project completely visible for replication? */ @@ -177,49 +182,60 @@ /** Is this user a project owner? Ownership does not imply {@link #isVisible()} */ public boolean isOwner() { - return controlForRef(RefRight.ALL).isOwner() + return controlForRef(AccessSection.ALL).isOwner() || getCurrentUser().isAdministrator(); } /** Does this user have ownership on at least one reference name? */ public boolean isOwnerAnyRef() { - return canPerformOnAnyRef(ApprovalCategory.OWN, (short) 1) + return canPerformOnAnyRef(Permission.OWNER) || getCurrentUser().isAdministrator(); } /** @return true if the user can upload to at least one reference */ public boolean canPushToAtLeastOneRef() { - return canPerformOnAnyRef(ApprovalCategory.READ, (short) 2) - || canPerformOnAnyRef(ApprovalCategory.PUSH_HEAD, (short) 1) - || canPerformOnAnyRef(ApprovalCategory.PUSH_TAG, (short) 1); + return canPerformOnAnyRef(Permission.PUSH) + || canPerformOnAnyRef(Permission.PUSH_TAG); } - // TODO (anatol.pomazau): Try to merge this method with similar RefRightsForPattern#canPerform - private boolean canPerformOnAnyRef(ApprovalCategory.Id actionId, - short requireValue) { - final Set<AccountGroup.Id> groups = user.getEffectiveGroups(); + private boolean canPerformOnAnyRef(String permissionName) { + final Set<AccountGroup.UUID> groups = user.getEffectiveGroups(); - for (final RefRight pr : state.getAllRights(actionId, true)) { - if (groups.contains(pr.getAccountGroupId()) - && pr.getMaxValue() >= requireValue) { - return true; + for (AccessSection section : access()) { + Permission permission = section.getPermission(permissionName); + if (permission == null) { + continue; + } + + for (PermissionRule rule : permission.getRules()) { + if (rule.getDeny()) { + continue; + } + + // Being in a group that was granted this permission is only an + // approximation. There might be overrides and doNotInherit + // that would render this to be false. + // + if (groups.contains(rule.getGroup().getUUID()) + && controlForRef(section.getRefPattern()).canPerform(permissionName)) { + return true; + } } } return false; } - private boolean canPerformOnAllRefs(ApprovalCategory.Id actionId, - short requireValue) { + private boolean canPerformOnAllRefs(String permission) { boolean canPerform = false; - final Set<String> patterns = allRefPatterns(actionId); - if (patterns.contains(RefRight.ALL)) { + Set<String> patterns = allRefPatterns(permission); + if (patterns.contains(AccessSection.ALL)) { // Only possible if granted on the pattern that // matches every possible reference. Check all // patterns also have the permission. // for (final String pattern : patterns) { - if (controlForRef(pattern).canPerform(actionId, requireValue)) { + if (controlForRef(pattern).canPerform(permission)) { canPerform = true; } else { return false; @@ -229,14 +245,24 @@ return canPerform; } - private Set<String> allRefPatterns(ApprovalCategory.Id actionId) { - final Set<String> all = new HashSet<String>(); - for (final RefRight pr : state.getAllRights(actionId, true)) { - all.add(pr.getRefPattern()); + private Set<String> allRefPatterns(String permissionName) { + Set<String> all = new HashSet<String>(); + for (AccessSection section : access()) { + Permission permission = section.getPermission(permissionName); + if (permission != null) { + all.add(section.getRefPattern()); + } } return all; } + Collection<AccessSection> access() { + if (access == null) { + access = state.getAllAccessSections(); + } + return access; + } + public boolean canRunUploadPack() { return isAnyIncludedIn(uploadGroups, user.getEffectiveGroups()); }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java index 0b8e83a..73765e8 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
@@ -14,13 +14,16 @@ package com.google.gerrit.server.project; +import com.google.gerrit.common.data.AccessSection; +import com.google.gerrit.common.data.GroupReference; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.reviewdb.AccountGroup; -import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.RefRight; import com.google.gerrit.server.AnonymousUser; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.config.WildProjectName; +import com.google.gerrit.server.git.ProjectConfig; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; @@ -28,15 +31,13 @@ import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; import java.util.List; import java.util.Set; /** Cached information on a project. */ public class ProjectState { public interface Factory { - ProjectState create(Project project, Collection<RefRight> localRights); + ProjectState create(ProjectConfig config); } private final AnonymousUser anonymousUser; @@ -44,92 +45,64 @@ private final ProjectCache projectCache; private final ProjectControl.AssistedFactory projectControlFactory; - private final Project project; - private final Collection<RefRight> localRights; - private final Set<AccountGroup.Id> localOwners; - - private volatile Collection<RefRight> inheritedRights; + private final ProjectConfig config; + private final Set<AccountGroup.UUID> localOwners; @Inject protected ProjectState(final AnonymousUser anonymousUser, final ProjectCache projectCache, @WildProjectName final Project.NameKey wildProject, final ProjectControl.AssistedFactory projectControlFactory, - @Assisted final Project project, - @Assisted Collection<RefRight> rights) { + @Assisted final ProjectConfig config) { this.anonymousUser = anonymousUser; this.projectCache = projectCache; this.wildProject = wildProject; this.projectControlFactory = projectControlFactory; + this.config = config; - if (wildProject.equals(project.getNameKey())) { - rights = new ArrayList<RefRight>(rights); - for (Iterator<RefRight> itr = rights.iterator(); itr.hasNext();) { - if (!itr.next().getApprovalCategoryId().canBeOnWildProject()) { - itr.remove(); + HashSet<AccountGroup.UUID> groups = new HashSet<AccountGroup.UUID>(); + AccessSection all = config.getAccessSection(AccessSection.ALL); + if (all != null) { + Permission owner = all.getPermission(Permission.OWNER); + if (owner != null) { + for (PermissionRule rule : owner.getRules()) { + GroupReference ref = rule.getGroup(); + if (ref.getUUID() != null) { + groups.add(ref.getUUID()); + } } } - rights = Collections.unmodifiableCollection(rights); - } - - this.project = project; - this.localRights = rights; - - final HashSet<AccountGroup.Id> groups = new HashSet<AccountGroup.Id>(); - for (final RefRight right : rights) { - if (ApprovalCategory.OWN.equals(right.getApprovalCategoryId()) - && right.getMaxValue() > 0 - && right.getRefPattern().equals(RefRight.ALL)) { - groups.add(right.getAccountGroupId()); - } } localOwners = Collections.unmodifiableSet(groups); } public Project getProject() { - return project; + return getConfig().getProject(); + } + + public ProjectConfig getConfig() { + return config; } /** Get the rights that pertain only to this project. */ - public Collection<RefRight> getLocalRights() { - return localRights; + public Collection<AccessSection> getLocalAccessSections() { + return getConfig().getAccessSections(); } - /** - * Get the rights that pertain only to this project. - * - * @param action the category requested. - * @return immutable collection of rights for the requested category. - */ - public Collection<RefRight> getLocalRights(ApprovalCategory.Id action) { - return filter(getLocalRights(), action); - } - - /** Get the rights this project inherits from the wild project. */ - public Collection<RefRight> getInheritedRights() { - if (inheritedRights == null) { - inheritedRights = computeInheritedRights(); - } - return inheritedRights; - } - - void setInheritedRights(Collection<RefRight> all) { - inheritedRights = all; - } - - private Collection<RefRight> computeInheritedRights() { - if (isSpecialWildProject()) { + /** Get the rights this project inherits. */ + public Collection<AccessSection> getInheritedAccessSections() { + if (isWildProject()) { return Collections.emptyList(); } - List<RefRight> inherited = new ArrayList<RefRight>(); + List<AccessSection> inherited = new ArrayList<AccessSection>(); Set<Project.NameKey> seen = new HashSet<Project.NameKey>(); - Project.NameKey parent = project.getParent(); + Project.NameKey parent = getProject().getParent(); while (parent != null && seen.add(parent)) { ProjectState s = projectCache.get(parent); if (s != null) { - inherited.addAll(s.getLocalRights()); + inherited.addAll(s.getLocalAccessSections()); parent = s.getProject().getParent(); } else { break; @@ -138,76 +111,21 @@ // Wild project is the parent, or the root of the tree if (parent == null) { - inherited.addAll(getWildProjectRights()); - } - - return Collections.unmodifiableCollection(inherited); - } - - private Collection<RefRight> getWildProjectRights() { - final ProjectState s = projectCache.get(wildProject); - return s != null ? s.getLocalRights() : Collections.<RefRight> emptyList(); - } - - /** - * Utility class that is needed to filter overridden refrights - */ - private static class Grant { - final AccountGroup.Id group; - final String pattern; - - private Grant(AccountGroup.Id group, String pattern) { - this.group = group; - this.pattern = pattern; - } - - @Override - public boolean equals(Object o) { - if (o == null) - return false; - Grant grant = (Grant) o; - return group.equals(grant.group) && pattern.equals(grant.pattern); - } - - @Override - public int hashCode() { - int result = group.hashCode(); - result = 31 * result + pattern.hashCode(); - return result; - } - } - - /** - * Get the rights this project has and inherits from the wild project. - * - * @param action the category requested. - * @param dropOverridden whether to remove inherited permissions in case if we have a - * local one that matches (action,group,ref) - * @return immutable collection of rights for the requested category. - */ - public Collection<RefRight> getAllRights(ApprovalCategory.Id action, boolean dropOverridden) { - Collection<RefRight> rights = new LinkedList<RefRight>(getLocalRights(action)); - rights.addAll(filter(getInheritedRights(), action)); - if (dropOverridden) { - Set<Grant> grants = new HashSet<Grant>(); - Iterator<RefRight> iter = rights.iterator(); - while (iter.hasNext()) { - RefRight right = iter.next(); - - Grant grant = new Grant(right.getAccountGroupId(), right.getRefPattern()); - if (grants.contains(grant)) { - iter.remove(); - } else { - grants.add(grant); - } + ProjectState s = projectCache.get(wildProject); + if (s != null) { + inherited.addAll(s.getLocalAccessSections()); } } - return Collections.unmodifiableCollection(rights); + + return inherited; } - /** Is this the special wild project which manages inherited rights? */ - public boolean isSpecialWildProject() { - return project.getNameKey().equals(wildProject); + /** Get both local and inherited access sections. */ + public Collection<AccessSection> getAllAccessSections() { + List<AccessSection> all = new ArrayList<AccessSection>(); + all.addAll(getLocalAccessSections()); + all.addAll(getInheritedAccessSections()); + return all; } /** @@ -216,13 +134,13 @@ * are no local owners the local owners of the nearest parent project * that has local owners are returned */ - public Set<AccountGroup.Id> getOwners() { - if (!localOwners.isEmpty() || isSpecialWildProject() - || project.getParent() == null) { + public Set<AccountGroup.UUID> getOwners() { + Project.NameKey parentName = getProject().getParent(); + if (!localOwners.isEmpty() || parentName == null || isWildProject()) { return localOwners; } - final ProjectState parent = projectCache.get(project.getParent()); + ProjectState parent = projectCache.get(parentName); if (parent != null) { return parent.getOwners(); } @@ -237,13 +155,23 @@ * owners) and all groups to which the owner privilege for 'refs/*' is * assigned for one of the parent projects (the inherited owners). */ - public Set<AccountGroup.Id> getAllOwners() { - final HashSet<AccountGroup.Id> owners = new HashSet<AccountGroup.Id>(); - for (final RefRight right : getAllRights(ApprovalCategory.OWN, true)) { - if (right.getMaxValue() > 0 && right.getRefPattern().equals(RefRight.ALL)) { - owners.add(right.getAccountGroupId()); + public Set<AccountGroup.UUID> getAllOwners() { + HashSet<AccountGroup.UUID> owners = new HashSet<AccountGroup.UUID>(); + owners.addAll(localOwners); + + Set<Project.NameKey> seen = new HashSet<Project.NameKey>(); + Project.NameKey parent = getProject().getParent(); + + while (parent != null && seen.add(parent)) { + ProjectState s = projectCache.get(parent); + if (s != null) { + owners.addAll(s.localOwners); + parent = s.getProject().getParent(); + } else { + break; } } + return Collections.unmodifiableSet(owners); } @@ -255,20 +183,7 @@ return projectControlFactory.create(user, this); } - private static Collection<RefRight> filter(Collection<RefRight> all, - ApprovalCategory.Id actionId) { - if (all.isEmpty()) { - return Collections.emptyList(); - } - final Collection<RefRight> mine = new ArrayList<RefRight>(all.size()); - for (final RefRight right : all) { - if (right.getApprovalCategoryId().equals(actionId)) { - mine.add(right); - } - } - if (mine.isEmpty()) { - return Collections.emptyList(); - } - return Collections.unmodifiableCollection(mine); + private boolean isWildProject() { + return wildProject.equals(getProject().getNameKey()); } }
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 8ddf585..ff1d09f 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,27 +14,16 @@ 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.CollectionsUtil; +import com.google.gerrit.common.data.AccessSection; import com.google.gerrit.common.data.ParamertizedString; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.common.data.PermissionRange; +import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.reviewdb.AccountGroup; -import com.google.gerrit.reviewdb.ApprovalCategory; -import com.google.gerrit.reviewdb.RefRight; -import com.google.gerrit.reviewdb.SystemConfig; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.IdentifiedUser; +import com.google.gerrit.server.git.GitRepositoryManager; import com.google.inject.Inject; import com.google.inject.assistedinject.Assisted; @@ -49,7 +38,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -57,8 +45,6 @@ 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; @@ -68,18 +54,18 @@ RefControl create(ProjectControl projectControl, String ref); } - private final SystemConfig systemConfig; private final ProjectControl projectControl; private final String refName; + private Map<String, List<PermissionRule>> permissions; + + private Boolean owner; private Boolean canForgeAuthor; private Boolean canForgeCommitter; @Inject - protected RefControl(final SystemConfig systemConfig, - @Assisted final ProjectControl projectControl, + protected RefControl(@Assisted final ProjectControl projectControl, @Assisted String ref) { - this.systemConfig = systemConfig; if (isRE(ref)) { ref = shortestExample(ref); @@ -114,26 +100,29 @@ /** 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 if (getRefName().equals( + AccessSection.ALL.substring(0, AccessSection.ALL.length() - 1))) { + // 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. + // + owner = getCurrentUser().isAdministrator(); + + } else { + owner = getProjectControl().isOwner(); + } } + return owner; } /** Can this user see this reference exists? */ public boolean isVisible() { return getProjectControl().visibleForReplication() - || canPerform(READ, (short) 1); + || canPerform(Permission.READ); } /** @@ -144,27 +133,66 @@ * ref */ public boolean canUpload() { - return canPerform(READ, (short) 2); + return getProjectControl() + .controlForRef("refs/for/" + getRefName()) + .canPerform(Permission.PUSH); } /** @return true if this user can submit merge patch sets to this ref */ public boolean canUploadMerges() { - return canPerform(READ, (short) 3); + return getProjectControl() + .controlForRef("refs/for/" + getRefName()) + .canPerform(Permission.PUSH_MERGE); } /** @return true if this user can submit patch sets to this ref */ 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 getProjectControl().isOwner(); + } + return canPerform(Permission.SUBMIT); } /** @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) + && !getProjectControl().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); } /** @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(); + } + + private boolean canPushWithForce() { + if (GitRepositoryManager.REF_CONFIG.equals(refName) + && !getProjectControl().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; + } + for (PermissionRule rule : access(Permission.PUSH)) { + if (rule.getForce()) { + return true; + } + } + return false; } /** @@ -186,7 +214,7 @@ } 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 +236,7 @@ } else { valid = false; } - if (!valid && !owner && !canPerform(FORGE_IDENTITY, FORGE_COMMITTER)) { + if (!valid && !owner && !canForgeCommitter()) { return false; } } @@ -217,9 +245,9 @@ // 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 +262,21 @@ * @return {@code true} if the user specified can delete a Git ref. */ public boolean canDelete() { + if (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 +286,7 @@ /** @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,314 +294,103 @@ /** @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); + return canPerform(Permission.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()); + /** 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 : permissions().entrySet()) { + if (Permission.isLabel(e.getKey())) { + r.add(toRange(e.getKey(), e.getValue())); } } - - 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)); } + return null; + } - public void addRight(RefRight right) { - rights.add(right); - if (right.isExclusive()) { - containsExclusive = true; - } + 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); + } - public List<RefRight> getRights() { - return Collections.unmodifiableList(rights); - } + /** True if the user has this permission. Works only for non labels. */ + boolean canPerform(String permissionName) { + return !access(permissionName).isEmpty(); + } - public boolean containsExclusive() { - return containsExclusive; - } + /** 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(); + } - /** - * 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; + /** All rules that pertain to this user, on this reference. */ + private Map<String, List<PermissionRule>> permissions() { + if (permissions == null) { + List<AccessSection> sections = new ArrayList<AccessSection>(); + for (AccessSection section : projectControl.access()) { + if (appliesToRef(section)) { + sections.add(section); } } - return false; - } - } + Collections.sort(sections, new MostSpecificComparator(getRefName())); - boolean canPerform(ApprovalCategory.Id actionId, short level) { - final Set<AccountGroup.Id> groups = getCurrentUser().getEffectiveGroups(); + Set<SeenRule> seen = new HashSet<SeenRule>(); + Set<String> exclusiveGroupPermissions = new HashSet<String>(); - List<RefRight> allRights = new ArrayList<RefRight>(); - allRights.addAll(getAllRights(actionId)); + permissions = new HashMap<String, List<PermissionRule>>(); + for (AccessSection section : sections) { + for (Permission permission : section.getPermissions()) { + if (exclusiveGroupPermissions.contains(permission.getName())) { + continue; + } - 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; - } - - /** - * 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; + for (PermissionRule rule : permission.getRules()) { + if (matchGroup(rule.getGroup().getUUID())) { + SeenRule s = new SeenRule(section, permission, rule); + if (seen.add(s) && !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); + } } } - 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; + if (permission.getExclusiveGroup()) { + exclusiveGroupPermissions.add(permission.getName()); } } - - private int transitions(String pattern) { - if (isRE(pattern)) { - return toRegExp(pattern).toAutomaton().getNumberOfTransitions(); - - } else if (pattern.endsWith("/*")) { - return pattern.length(); - - } else { - return pattern.length(); - } - } - }; - - /** - * Sorts all given rights into a map, ordered by descending length of - * ref pattern. - * - * For example, if given the following rights in argument: - * - * ["refs/heads/master", group1, -1, +1], - * ["refs/heads/master", group2, -2, +2], - * ["refs/heads/*", group3, -1, +1] - * ["refs/heads/stable", group2, -1, +1] - * - * Then the following map is returned: - * "refs/heads/master" => { - * ["refs/heads/master", group1, -1, +1], - * ["refs/heads/master", group2, -2, +2] - * } - * "refs/heads/stable" => {["refs/heads/stable", group2, -1, +1]} - * "refs/heads/*" => {["refs/heads/*", group3, -1, +1]} - * - * @param actionRights - * @return A sorted map keyed off the ref pattern of all rights. - */ - private SortedMap<String, RefRightsForPattern> sortedRightsByPattern( - List<RefRight> actionRights) { - SortedMap<String, RefRightsForPattern> rights = - new TreeMap<String, RefRightsForPattern>(BY_MOST_SPECIFIC_SORT); - for (RefRight actionRight : actionRights) { - RefRightsForPattern patternRights = - rights.get(actionRight.getRefPattern()); - if (patternRights == null) { - patternRights = new RefRightsForPattern(); - rights.put(actionRight.getRefPattern(), patternRights); - } - patternRights.addRight(actionRight); - } - return rights; - } - - private List<RefRight> getAllRights(ApprovalCategory.Id actionId) { - final List<RefRight> allRefRights = filter(getProjectState().getAllRights(actionId, true)); - return resolveOwnerGroups(allRefRights); - } - - /** - * Returns all applicable rights for a given approval category. - * - * Applicable rights are defined as the list of {@code RefRight}s which match - * the ref for which this object was created, stopping the ref wildcard - * matching when an exclusive ref right was encountered, for the given - * approval category. - * @param id The {@link ApprovalCategory.Id}. - * @return All applicable rights. - */ - public List<RefRight> getApplicableRights(final ApprovalCategory.Id id) { - List<RefRight> l = new ArrayList<RefRight>(); - l.addAll(getAllRights(id)); - SortedMap<String, RefRightsForPattern> perPatternRights = - sortedRightsByPattern(l); - List<RefRight> applicable = new ArrayList<RefRight>(); - for (RefRightsForPattern patternRights : perPatternRights.values()) { - applicable.addAll(patternRights.getRights()); - if (patternRights.containsExclusive()) { - break; } } - return Collections.unmodifiableList(applicable); + return permissions; } - /** - * 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; - } + private boolean appliesToRef(AccessSection section) { + String refPattern = section.getRefPattern(); - /** - * 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); - } - return resolvedRefRights; - } - - private List<RefRight> filter(Collection<RefRight> all) { - List<RefRight> mine = new ArrayList<RefRight>(all.size()); - for (RefRight right : all) { - if (matches(right.getRefPattern())) { - mine.add(right); - } - } - return mine; - } - - private ProjectState getProjectState() { - return projectControl.getProjectState(); - } - - private boolean matches(String refPattern) { if (isTemplate(refPattern)) { ParamertizedString template = new ParamertizedString(refPattern); HashMap<String, String> p = new HashMap<String, String>(); @@ -599,6 +425,18 @@ } } + private boolean matchGroup(AccountGroup.UUID uuid) { + Set<AccountGroup.UUID> userGroups = getCurrentUser().getEffectiveGroups(); + + if (AccountGroup.PROJECT_OWNERS.equals(uuid)) { + ProjectState state = projectControl.getProjectState(); + return CollectionsUtil.isAnyIncludedIn(state.getAllOwners(), userGroups); + + } else { + return userGroups.contains(uuid); + } + } + private static boolean isTemplate(String refPattern) { return 0 <= refPattern.indexOf("${"); } @@ -611,7 +449,7 @@ } private static boolean isRE(String refPattern) { - return refPattern.startsWith(RefRight.REGEX_PREFIX); + return refPattern.startsWith(AccessSection.REGEX_PREFIX); } public static String shortestExample(String pattern) { @@ -630,4 +468,143 @@ } return new RegExp(refPattern, RegExp.NONE); } + + /** Tracks whether or not a permission has been overridden. */ + private static class SeenRule { + final String refPattern; + final String permissionName; + final AccountGroup.UUID group; + + SeenRule(AccessSection section, Permission permission, PermissionRule rule) { + refPattern = section.getRefPattern(); + permissionName = permission.getName(); + group = rule.getGroup().getUUID(); + } + + @Override + public int hashCode() { + int hc = refPattern.hashCode(); + hc = hc * 31 + permissionName.hashCode(); + if (group != null) { + hc = hc * 31 + group.hashCode(); + } + return hc; + } + + @Override + public boolean equals(Object other) { + if (other instanceof SeenRule) { + SeenRule a = this; + SeenRule b = (SeenRule) other; + return a.refPattern.equals(b.refPattern) // + && a.permissionName.equals(b.permissionName) // + && eq(a.group, b.group); + } + return false; + } + + private boolean eq(AccountGroup.UUID a, AccountGroup.UUID b) { + return a != null && b != null && a.equals(b); + } + } + + /** + * Order the Ref Pattern by the most specific. This sort is done by: + * <ul> + * <li>1 - The minor value of Levenshtein string distance between the branch + * name and the regex string shortest example. A shorter distance is a more + * specific match. + * <li>2 - Finites first, infinities after. + * <li>3 - Number of transitions. + * <li>4 - Length of the expression text. + * </ul> + * + * Levenshtein distance is a measure of the similarity between two strings. + * The distance is the number of deletions, insertions, or substitutions + * required to transform one string into another. + * + * For example, if given refs/heads/m* and refs/heads/*, the distances are 5 + * and 6. It means that refs/heads/m* is more specific because it's closer to + * refs/heads/master than refs/heads/*. + * + * Another example could be refs/heads/* and refs/heads/[a-zA-Z]*, the + * distances are both 6. Both are infinite, but refs/heads/[a-zA-Z]* has more + * transitions, which after all turns it more specific. + */ + private static final class MostSpecificComparator implements + Comparator<AccessSection> { + private final String refName; + + MostSpecificComparator(String refName) { + this.refName = refName; + } + + public int compare(AccessSection a, AccessSection b) { + return compare(a.getRefPattern(), b.getRefPattern()); + } + + private int compare(final String pattern1, final String pattern2) { + int cmp = distance(pattern1) - distance(pattern2); + if (cmp == 0) { + boolean p1_finite = finite(pattern1); + boolean p2_finite = finite(pattern2); + + if (p1_finite && !p2_finite) { + cmp = -1; + } else if (!p1_finite && p2_finite) { + cmp = 1; + } else /* if (f1 == f2) */{ + cmp = 0; + } + } + if (cmp == 0) { + cmp = transitions(pattern1) - transitions(pattern2); + } + if (cmp == 0) { + cmp = pattern2.length() - pattern1.length(); + } + return cmp; + } + + private int distance(String pattern) { + String example; + if (isRE(pattern)) { + example = shortestExample(pattern); + + } else if (pattern.endsWith("/*")) { + example = pattern.substring(0, pattern.length() - 1) + '1'; + + } else if (pattern.equals(refName)) { + return 0; + + } else { + return Math.max(pattern.length(), refName.length()); + } + return StringUtils.getLevenshteinDistance(example, refName); + } + + private boolean finite(String pattern) { + if (isRE(pattern)) { + return toRegExp(pattern).toAutomaton().isFinite(); + + } else if (pattern.endsWith("/*")) { + return false; + + } else { + return true; + } + } + + private int transitions(String pattern) { + if (isRE(pattern)) { + return toRegExp(pattern).toAutomaton().getNumberOfTransitions(); + + } else if (pattern.endsWith("/*")) { + return pattern.length(); + + } else { + return pattern.length(); + } + } + } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java index 577c5af..074ad19 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -30,6 +30,7 @@ import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.patch.PatchListCache; import com.google.gerrit.server.project.ChangeControl; +import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.query.IntPredicate; import com.google.gerrit.server.query.Predicate; import com.google.gerrit.server.query.QueryBuilder; @@ -107,6 +108,7 @@ final Project.NameKey wildProjectName; final PatchListCache patchListCache; final GitRepositoryManager repoManager; + final ProjectCache projectCache; @Inject Arguments(Provider<ReviewDb> dbProvider, @@ -118,7 +120,8 @@ AuthConfig authConfig, ApprovalTypes approvalTypes, @WildProjectName Project.NameKey wildProjectName, PatchListCache patchListCache, - GitRepositoryManager repoManager) { + GitRepositoryManager repoManager, + ProjectCache projectCache) { this.dbProvider = dbProvider; this.rewriter = rewriter; this.userFactory = userFactory; @@ -131,6 +134,7 @@ this.wildProjectName = wildProjectName; this.patchListCache = patchListCache; this.repoManager = repoManager; + this.projectCache = projectCache; } } @@ -340,15 +344,15 @@ // AccountGroup g = args.groupCache.get(new AccountGroup.NameKey(who)); if (g != null) { - return visibleto(new SingleGroupUser(args.authConfig, g.getId())); + return visibleto(new SingleGroupUser(args.authConfig, g.getGroupUUID())); } Collection<AccountGroup> matches = args.groupCache.get(new AccountGroup.ExternalNameKey(who)); if (matches != null && !matches.isEmpty()) { - HashSet<AccountGroup.Id> ids = new HashSet<AccountGroup.Id>(); + HashSet<AccountGroup.UUID> ids = new HashSet<AccountGroup.UUID>(); for (AccountGroup group : matches) { - ids.add(group.getId()); + ids.add(group.getGroupUUID()); } return visibleto(new SingleGroupUser(args.authConfig, ids)); } @@ -508,23 +512,19 @@ // Try to match a project name by substring query. final List<ProjectPredicate> predicate = new ArrayList<ProjectPredicate>(); - try { - for (final Project p : args.dbProvider.get().projects().all()) { - if (p.getName().toLowerCase().contains(query.toLowerCase())) { - predicate.add(new ProjectPredicate(args.dbProvider, p.getName())); - } + for (Project.NameKey name : args.projectCache.all()) { + if (name.get().toLowerCase().contains(query.toLowerCase())) { + predicate.add(new ProjectPredicate(args.dbProvider, name.get())); } + } - // If two or more projects contains "query" as substring create an - // OrPredicate holding predicates for all these projects, otherwise if - // only one contains that, return only that one predicate by itself. - if (predicate.size() == 1) { - return predicate.get(0); - } else if (predicate.size() > 1) { - return Predicate.or(predicate); - } - } catch (OrmException e) { - throw error("Cannot lookup project.", e); + // If two or more projects contains "query" as substring create an + // OrPredicate holding predicates for all these projects, otherwise if + // only one contains that, return only that one predicate by itself. + if (predicate.size() == 1) { + return predicate.get(0); + } else if (predicate.size() > 1) { + return Predicate.or(predicate); } throw error("Unsupported query:" + query);
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java index 717b487..b31bf65 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/ChangeQueryRewriter.java
@@ -38,7 +38,8 @@ new ChangeQueryBuilder.Arguments( // new InvalidProvider<ReviewDb>(), // new InvalidProvider<ChangeQueryRewriter>(), // - null, null, null, null, null, null, null, null, null, null), null)); + null, null, null, null, null, null, null, // + null, null, null, null), null)); private final Provider<ReviewDb> dbProvider;
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java index e76c278..c5c974d 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/LabelPredicate.java
@@ -16,6 +16,7 @@ import com.google.gerrit.common.data.ApprovalType; import com.google.gerrit.common.data.ApprovalTypes; +import com.google.gerrit.common.data.Permission; import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.PatchSetApproval; import com.google.gerrit.reviewdb.ReviewDb; @@ -33,48 +34,54 @@ private static enum Test { EQ { @Override - public boolean match(short psValue, short expValue) { + public boolean match(int psValue, int expValue) { return psValue == expValue; } }, GT_EQ { @Override - public boolean match(short psValue, short expValue) { + public boolean match(int psValue, int expValue) { return psValue >= expValue; } }, LT_EQ { @Override - public boolean match(short psValue, short expValue) { + public boolean match(int psValue, int expValue) { return psValue <= expValue; } }; - abstract boolean match(short psValue, short expValue); + abstract boolean match(int psValue, int expValue); } - private static ApprovalCategory.Id category(ApprovalTypes types, String toFind) { - if (types.getApprovalType(new ApprovalCategory.Id(toFind)) != null) { - return new ApprovalCategory.Id(toFind); + private static ApprovalCategory category(ApprovalTypes types, String toFind) { + if (types.byLabel(toFind) != null) { + return types.byLabel(toFind).getCategory(); + } + + if (types.byId(new ApprovalCategory.Id(toFind)) != null) { + return types.byId(new ApprovalCategory.Id(toFind)).getCategory(); } for (ApprovalType at : types.getApprovalTypes()) { - String name = at.getCategory().getName(); - if (toFind.equalsIgnoreCase(name)) { - return at.getCategory().getId(); + ApprovalCategory category = at.getCategory(); - } else if (toFind.equalsIgnoreCase(name.replace(" ", ""))) { - return at.getCategory().getId(); + if (toFind.equalsIgnoreCase(category.getName())) { + return category; + + } else if (toFind.equalsIgnoreCase(category.getName().replace(" ", ""))) { + return category; } } for (ApprovalType at : types.getApprovalTypes()) { - if (toFind.equalsIgnoreCase(at.getCategory().getAbbreviatedName())) { - return at.getCategory().getId(); + ApprovalCategory category = at.getCategory(); + if (toFind.equalsIgnoreCase(category.getAbbreviatedName())) { + return category; } } - return new ApprovalCategory.Id(toFind); + return new ApprovalCategory(new ApprovalCategory.Id(toFind), toFind); } private static Test op(String op) { @@ -92,19 +99,20 @@ } } - private static short value(String value) { + private static int value(String value) { if (value.startsWith("+")) { value = value.substring(1); } - return Short.parseShort(value); + return Integer.parseInt(value); } private final ChangeControl.GenericFactory ccFactory; private final IdentifiedUser.GenericFactory userFactory; private final Provider<ReviewDb> dbProvider; private final Test test; - private final ApprovalCategory.Id category; - private final short expVal; + private final ApprovalCategory category; + private final String permissionName; + private final int expVal; LabelPredicate(ChangeControl.GenericFactory ccFactory, IdentifiedUser.GenericFactory userFactory, Provider<ReviewDb> dbProvider, @@ -131,13 +139,15 @@ test = Test.EQ; expVal = 1; } + + this.permissionName = Permission.forLabel(category.getLabelName()); } @Override public boolean match(final ChangeData object) throws OrmException { for (PatchSetApproval p : object.currentApprovals(dbProvider)) { if (p.getCategoryId().equals(category)) { - short psVal = p.getValue(); + int psVal = p.getValue(); if (test.match(psVal, expVal)) { // Double check the value is still permitted for the user. // @@ -149,7 +159,7 @@ // continue; } - psVal = cc.normalize(category, psVal); + psVal = cc.getRange(permissionName).squash(psVal); } catch (NoSuchChangeException e) { // The project has disappeared. //
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/SingleGroupUser.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/SingleGroupUser.java index 2fb6694..1c37ac4 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/SingleGroupUser.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/SingleGroupUser.java
@@ -26,19 +26,19 @@ import java.util.Set; final class SingleGroupUser extends CurrentUser { - private final Set<AccountGroup.Id> groups; + private final Set<AccountGroup.UUID> groups; - SingleGroupUser(AuthConfig authConfig, AccountGroup.Id groupId) { + SingleGroupUser(AuthConfig authConfig, AccountGroup.UUID groupId) { this(authConfig, Collections.singleton(groupId)); } - SingleGroupUser(AuthConfig authConfig, Set<AccountGroup.Id> groups) { + SingleGroupUser(AuthConfig authConfig, Set<AccountGroup.UUID> groups) { super(AccessPath.UNKNOWN, authConfig); this.groups = groups; } @Override - public Set<AccountGroup.Id> getEffectiveGroups() { + public Set<AccountGroup.UUID> getEffectiveGroups() { return groups; }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/DatabaseModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/DatabaseModule.java index 55a36d6..6b66e33 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/DatabaseModule.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/DatabaseModule.java
@@ -26,8 +26,6 @@ public class DatabaseModule extends FactoryModule { @Override protected void configure() { - install(new SchemaVersion.Module()); - bind(new TypeLiteral<SchemaFactory<ReviewDb>>() {}).to( new TypeLiteral<Database<ReviewDb>>() {}).in(SINGLETON); bind(new TypeLiteral<Database<ReviewDb>>() {}).toProvider(
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java index a24471a..c5ca308 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaCreator.java
@@ -14,19 +14,26 @@ package com.google.gerrit.server.schema; +import com.google.gerrit.common.Version; +import com.google.gerrit.common.data.AccessSection; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.reviewdb.AccountGroup; import com.google.gerrit.reviewdb.AccountGroupName; import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.ApprovalCategoryValue; import com.google.gerrit.reviewdb.CurrentSchemaVersion; import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.RefRight; import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.reviewdb.SystemConfig; +import com.google.gerrit.server.GerritPersonIdent; +import com.google.gerrit.server.account.GroupUUID; import com.google.gerrit.server.config.SitePath; import com.google.gerrit.server.config.SitePaths; -import com.google.gerrit.server.workflow.NoOpFunction; -import com.google.gerrit.server.workflow.SubmitFunction; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.git.MetaDataUpdate; +import com.google.gerrit.server.git.NoReplication; +import com.google.gerrit.server.git.ProjectConfig; import com.google.gwtjsonrpc.server.SignedToken; import com.google.gwtorm.client.OrmException; import com.google.gwtorm.jdbc.JdbcExecutor; @@ -37,6 +44,11 @@ import com.google.gwtorm.schema.sql.SqlDialect; import com.google.inject.Inject; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.Repository; + import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -50,27 +62,40 @@ private final @SitePath File site_path; + private final GitRepositoryManager mgr; + private final PersonIdent serverUser; + private final int versionNbr; private final ScriptRunner index_generic; private final ScriptRunner index_postgres; private final ScriptRunner mysql_nextval; + private AccountGroup admin; + private AccountGroup anonymous; + private AccountGroup registered; + private AccountGroup owners; + @Inject public SchemaCreator(final SitePaths site, - @Current final SchemaVersion version) { - this(site.site_path, version); + @Current final SchemaVersion version, final GitRepositoryManager mgr, + @GerritPersonIdent final PersonIdent au) { + this(site.site_path, version, mgr, au); } public SchemaCreator(final @SitePath File site, - @Current final SchemaVersion version) { + @Current final SchemaVersion version, final GitRepositoryManager gitMgr, + final @GerritPersonIdent PersonIdent au) { site_path = site; + mgr = gitMgr; + serverUser = au; versionNbr = version.getVersionNbr(); index_generic = new ScriptRunner("index_generic.sql"); index_postgres = new ScriptRunner("index_postgres.sql"); mysql_nextval = new ScriptRunner("mysql_nextval.sql"); } - public void create(final ReviewDb db) throws OrmException { + public void create(final ReviewDb db) throws OrmException, IOException, + ConfigInvalidException { final JdbcSchema jdbc = (JdbcSchema) db; final JdbcExecutor e = new JdbcExecutor(jdbc); try { @@ -84,15 +109,13 @@ db.schemaVersion().insert(Collections.singleton(sVer)); final SystemConfig sConfig = initSystemConfig(db); - initOwnerCategory(db); - initReadCategory(db, sConfig); initVerifiedCategory(db); initCodeReviewCategory(db, sConfig); - initSubmitCategory(db); - initPushTagCategory(db); - initPushUpdateBranchCategory(db); - initForgeIdentityCategory(db, sConfig); - initWildCardProject(db); + + if (mgr != null) { + // TODO This should never be null when initializing a site. + initWildCardProject(); + } final SqlDialect d = jdbc.getDialect(); if (d instanceof DialectH2) { @@ -110,19 +133,27 @@ } } + private AccountGroup newGroup(ReviewDb c, String name, AccountGroup.UUID uuid) + throws OrmException { + if (uuid == null) { + uuid = GroupUUID.make(name, serverUser); + } + return new AccountGroup( // + new AccountGroup.NameKey(name), // + new AccountGroup.Id(c.nextAccountGroupId()), // + uuid); + } + private SystemConfig initSystemConfig(final ReviewDb c) throws OrmException { - final AccountGroup admin = - new AccountGroup(new AccountGroup.NameKey("Administrators"), - new AccountGroup.Id(c.nextAccountGroupId())); + admin = newGroup(c, "Administrators", null); admin.setDescription("Gerrit Site Administrators"); admin.setType(AccountGroup.Type.INTERNAL); c.accountGroups().insert(Collections.singleton(admin)); c.accountGroupNames().insert( Collections.singleton(new AccountGroupName(admin))); - final AccountGroup anonymous = - new AccountGroup(new AccountGroup.NameKey("Anonymous Users"), - new AccountGroup.Id(c.nextAccountGroupId())); + anonymous = + newGroup(c, "Anonymous Users", AccountGroup.ANONYMOUS_USERS); anonymous.setDescription("Any user, signed-in or not"); anonymous.setOwnerGroupId(admin.getId()); anonymous.setType(AccountGroup.Type.SYSTEM); @@ -130,9 +161,8 @@ c.accountGroupNames().insert( Collections.singleton(new AccountGroupName(anonymous))); - final AccountGroup registered = - new AccountGroup(new AccountGroup.NameKey("Registered Users"), - new AccountGroup.Id(c.nextAccountGroupId())); + registered = + newGroup(c, "Registered Users", AccountGroup.REGISTERED_USERS); registered.setDescription("Any signed-in user"); registered.setOwnerGroupId(admin.getId()); registered.setType(AccountGroup.Type.SYSTEM); @@ -140,9 +170,7 @@ c.accountGroupNames().insert( Collections.singleton(new AccountGroupName(registered))); - final AccountGroup batchUsers = - new AccountGroup(new AccountGroup.NameKey("Non-Interactive Users"), - new AccountGroup.Id(c.nextAccountGroupId())); + final AccountGroup batchUsers = newGroup(c, "Non-Interactive Users", null); batchUsers.setDescription("Users who perform batch actions on Gerrit"); batchUsers.setOwnerGroupId(admin.getId()); batchUsers.setType(AccountGroup.Type.INTERNAL); @@ -150,9 +178,7 @@ c.accountGroupNames().insert( Collections.singleton(new AccountGroupName(batchUsers))); - final AccountGroup owners = - new AccountGroup(new AccountGroup.NameKey("Project Owners"), - new AccountGroup.Id(c.nextAccountGroupId())); + owners = newGroup(c, "Project Owners", AccountGroup.PROJECT_OWNERS); owners.setDescription("Any owner of the project"); owners.setOwnerGroupId(admin.getId()); owners.setType(AccountGroup.Type.SYSTEM); @@ -162,10 +188,17 @@ final SystemConfig s = SystemConfig.create(); s.registerEmailPrivateKey = SignedToken.generateRandomKey(); + s.adminGroupId = admin.getId(); + s.adminGroupUUID = admin.getGroupUUID(); + s.anonymousGroupId = anonymous.getId(); + s.registeredGroupId = registered.getId(); + s.batchUsersGroupId = batchUsers.getId(); + s.batchUsersGroupUUID = batchUsers.getGroupUUID(); + s.ownerGroupId = owners.getId(); s.wildProjectName = DEFAULT_WILD_NAME; try { @@ -177,13 +210,64 @@ return s; } - private void initWildCardProject(final ReviewDb c) throws OrmException { - final Project p; + private void initWildCardProject() throws IOException, ConfigInvalidException { + Repository git; + try { + git = mgr.openRepository(DEFAULT_WILD_NAME); + } catch (RepositoryNotFoundException notFound) { + // A repository may be missing if this project existed only to store + // inheritable permissions. For example '-- All Projects --'. + try { + git = mgr.createRepository(DEFAULT_WILD_NAME); + } catch (RepositoryNotFoundException err) { + final String name = DEFAULT_WILD_NAME.get(); + throw new IOException("Cannot create repository " + name, err); + } + } + try { + MetaDataUpdate md = + new MetaDataUpdate(new NoReplication(), DEFAULT_WILD_NAME, git); + md.getCommitBuilder().setAuthor(serverUser); + md.getCommitBuilder().setCommitter(serverUser); - p = new Project(DEFAULT_WILD_NAME); - p.setDescription("Rights inherited by all other projects"); - p.setUseContributorAgreements(false); - c.projects().insert(Collections.singleton(p)); + ProjectConfig config = ProjectConfig.read(md); + Project p = config.getProject(); + p.setDescription("Rights inherited by all other projects"); + p.setUseContributorAgreements(false); + + AccessSection all = config.getAccessSection(AccessSection.ALL, true); + AccessSection heads = config.getAccessSection(AccessSection.HEADS, true); + AccessSection meta = config.getAccessSection(GitRepositoryManager.REF_CONFIG, true); + + PermissionRule review = rule(config, registered); + review.setRange(-1, 1); + heads.getPermission(Permission.LABEL + "Code-Review", true).add(review); + + all.getPermission(Permission.READ, true) // + .add(rule(config, admin)); + all.getPermission(Permission.READ, true) // + .add(rule(config, anonymous)); + + config.getAccessSection("refs/for/" + AccessSection.ALL, true) // + .getPermission(Permission.PUSH, true) // + .add(rule(config, registered)); + all.getPermission(Permission.FORGE_AUTHOR, true) // + .add(rule(config, registered)); + + meta.getPermission(Permission.READ, true) // + .add(rule(config, owners)); + + md.setMessage("Initialized Gerrit Code Review " + Version.getVersion()); + if (!config.commit(md)) { + throw new IOException("Cannot create " + DEFAULT_WILD_NAME.get()); + } + } finally { + git.close(); + } + } + + private PermissionRule rule(ProjectConfig config, AccountGroup group) { + return new PermissionRule(config.resolve(group)); } private void initVerifiedCategory(final ReviewDb c) throws OrmException { @@ -218,143 +302,6 @@ vals.add(value(cat, -2, "Do not submit")); c.approvalCategories().insert(Collections.singleton(cat)); c.approvalCategoryValues().insert(vals); - - final RefRight approve = - new RefRight(new RefRight.Key(DEFAULT_WILD_NAME, - new RefRight.RefPattern("refs/heads/*"), cat.getId(), - sConfig.registeredGroupId)); - approve.setMaxValue((short) 1); - approve.setMinValue((short) -1); - c.refRights().insert(Collections.singleton(approve)); - } - - private void initOwnerCategory(final ReviewDb c) throws OrmException { - final ApprovalCategory cat; - final ArrayList<ApprovalCategoryValue> vals; - - cat = new ApprovalCategory(ApprovalCategory.OWN, "Owner"); - cat.setPosition((short) -1); - cat.setFunctionName(NoOpFunction.NAME); - vals = new ArrayList<ApprovalCategoryValue>(); - vals.add(value(cat, 1, "Administer All Settings")); - c.approvalCategories().insert(Collections.singleton(cat)); - c.approvalCategoryValues().insert(vals); - } - - private void initReadCategory(final ReviewDb c, final SystemConfig sConfig) - throws OrmException { - final ApprovalCategory cat; - final ArrayList<ApprovalCategoryValue> vals; - - cat = new ApprovalCategory(ApprovalCategory.READ, "Read Access"); - cat.setPosition((short) -1); - cat.setFunctionName(NoOpFunction.NAME); - vals = new ArrayList<ApprovalCategoryValue>(); - vals.add(value(cat, 3, "Upload merges permission")); - vals.add(value(cat, 2, "Upload permission")); - vals.add(value(cat, 1, "Read access")); - vals.add(value(cat, -1, "No access")); - c.approvalCategories().insert(Collections.singleton(cat)); - c.approvalCategoryValues().insert(vals); - - final RefRight.RefPattern pattern = new RefRight.RefPattern(RefRight.ALL); - { - final RefRight read = - new RefRight(new RefRight.Key(DEFAULT_WILD_NAME, pattern, - cat.getId(), sConfig.anonymousGroupId)); - read.setMaxValue((short) 1); - read.setMinValue((short) 1); - c.refRights().insert(Collections.singleton(read)); - } - { - final RefRight read = - new RefRight(new RefRight.Key(DEFAULT_WILD_NAME, pattern, - cat.getId(), sConfig.registeredGroupId)); - read.setMaxValue((short) 2); - read.setMinValue((short) 1); - c.refRights().insert(Collections.singleton(read)); - } - { - final RefRight read = - new RefRight(new RefRight.Key(DEFAULT_WILD_NAME, pattern, - cat.getId(), sConfig.adminGroupId)); - read.setMaxValue((short) 1); - read.setMinValue((short) 1); - c.refRights().insert(Collections.singleton(read)); - } - } - - private void initSubmitCategory(final ReviewDb c) throws OrmException { - final ApprovalCategory cat; - final ArrayList<ApprovalCategoryValue> vals; - - cat = new ApprovalCategory(ApprovalCategory.SUBMIT, "Submit"); - cat.setPosition((short) -1); - cat.setFunctionName(SubmitFunction.NAME); - vals = new ArrayList<ApprovalCategoryValue>(); - vals.add(value(cat, 1, "Submit")); - c.approvalCategories().insert(Collections.singleton(cat)); - c.approvalCategoryValues().insert(vals); - } - - private void initPushTagCategory(final ReviewDb c) throws OrmException { - final ApprovalCategory cat; - final ArrayList<ApprovalCategoryValue> vals; - - cat = new ApprovalCategory(ApprovalCategory.PUSH_TAG, "Push Tag"); - cat.setPosition((short) -1); - cat.setFunctionName(NoOpFunction.NAME); - vals = new ArrayList<ApprovalCategoryValue>(); - vals.add(value(cat, ApprovalCategory.PUSH_TAG_SIGNED, "Create Signed Tag")); - vals.add(value(cat, ApprovalCategory.PUSH_TAG_ANNOTATED, - "Create Annotated Tag")); - c.approvalCategories().insert(Collections.singleton(cat)); - c.approvalCategoryValues().insert(vals); - } - - private void initPushUpdateBranchCategory(final ReviewDb c) - throws OrmException { - final ApprovalCategory cat; - final ArrayList<ApprovalCategoryValue> vals; - - cat = new ApprovalCategory(ApprovalCategory.PUSH_HEAD, "Push Branch"); - cat.setPosition((short) -1); - cat.setFunctionName(NoOpFunction.NAME); - vals = new ArrayList<ApprovalCategoryValue>(); - vals.add(value(cat, ApprovalCategory.PUSH_HEAD_UPDATE, "Update Branch")); - vals.add(value(cat, ApprovalCategory.PUSH_HEAD_CREATE, "Create Branch")); - vals.add(value(cat, ApprovalCategory.PUSH_HEAD_REPLACE, - "Force Push Branch; Delete Branch")); - c.approvalCategories().insert(Collections.singleton(cat)); - c.approvalCategoryValues().insert(vals); - } - - private void initForgeIdentityCategory(final ReviewDb c, - final SystemConfig sConfig) throws OrmException { - final ApprovalCategory cat; - final ArrayList<ApprovalCategoryValue> values; - - cat = - new ApprovalCategory(ApprovalCategory.FORGE_IDENTITY, "Forge Identity"); - cat.setPosition((short) -1); - cat.setFunctionName(NoOpFunction.NAME); - values = new ArrayList<ApprovalCategoryValue>(); - values.add(value(cat, ApprovalCategory.FORGE_AUTHOR, - "Forge Author Identity")); - values.add(value(cat, ApprovalCategory.FORGE_COMMITTER, - "Forge Committer or Tagger Identity")); - values.add(value(cat, ApprovalCategory.FORGE_SERVER, - "Forge Gerrit Code Review Server Identity")); - c.approvalCategories().insert(Collections.singleton(cat)); - c.approvalCategoryValues().insert(values); - - RefRight right = - new RefRight(new RefRight.Key(sConfig.wildProjectName, - new RefRight.RefPattern(RefRight.ALL), - ApprovalCategory.FORGE_IDENTITY, sConfig.registeredGroupId)); - right.setMinValue(ApprovalCategory.FORGE_AUTHOR); - right.setMaxValue(ApprovalCategory.FORGE_AUTHOR); - c.refRights().insert(Collections.singleton(right)); } private static ApprovalCategoryValue value(final ApprovalCategory cat,
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaModule.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaModule.java new file mode 100644 index 0000000..3225e13 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaModule.java
@@ -0,0 +1,43 @@ +// Copyright (C) 2009 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.schema; + +import com.google.gerrit.lifecycle.LifecycleModule; +import com.google.gerrit.server.GerritPersonIdent; +import com.google.gerrit.server.GerritPersonIdentProvider; +import com.google.gerrit.server.config.FactoryModule; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.git.LocalDiskRepositoryManager; + +import org.eclipse.jgit.lib.PersonIdent; + +/** Validate the schema and connect to Git. */ +public class SchemaModule extends FactoryModule { + @Override + protected void configure() { + install(new SchemaVersion.Module()); + + bind(PersonIdent.class).annotatedWith(GerritPersonIdent.class).toProvider( + GerritPersonIdentProvider.class); + + bind(GitRepositoryManager.class).to(LocalDiskRepositoryManager.class); + install(new LifecycleModule() { + @Override + protected void configure() { + listener().to(LocalDiskRepositoryManager.Lifecycle.class); + } + }); + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java index f44eff5..3937aaa 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaUpdater.java
@@ -23,6 +23,8 @@ import com.google.inject.Inject; import com.google.inject.Provider; +import org.eclipse.jgit.errors.ConfigInvalidException; + import java.io.IOException; import java.sql.SQLException; import java.util.Collections; @@ -49,7 +51,13 @@ final SchemaVersion u = updater.get(); final CurrentSchemaVersion version = getSchemaVersion(db); if (version == null) { - creator.create(db); + try { + creator.create(db); + } catch (IOException e) { + throw new OrmException("Cannot initialize schema", e); + } catch (ConfigInvalidException e) { + throw new OrmException("Cannot initialize schema", e); + } } else { try {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java index 978a152..b83f540 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/SchemaVersion.java
@@ -32,7 +32,7 @@ /** A version of the database schema. */ public abstract class SchemaVersion { /** The current schema version. */ - private static final Class<? extends SchemaVersion> C = Schema_52.class; + private static final Class<? extends SchemaVersion> C = Schema_53.class; public static class Module extends AbstractModule { @Override
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_19.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_19.java deleted file mode 100644 index 7ad91ff..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_19.java +++ /dev/null
@@ -1,42 +0,0 @@ -// Copyright (C) 2009 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.schema; - -import com.google.gerrit.reviewdb.CurrentSchemaVersion; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gwtorm.client.OrmException; -import com.google.inject.Inject; -import com.google.inject.Provider; -import com.google.inject.ProvisionException; - -class Schema_19 extends SchemaVersion { - @Inject - Schema_19() { - super(new Provider<SchemaVersion>() { - public SchemaVersion get() { - throw new ProvisionException("Cannot upgrade from 18"); - } - }); - } - - @Override - protected void upgradeFrom(UpdateUI ui, CurrentSchemaVersion curr, - ReviewDb db, boolean toTargetVersion) throws OrmException { - throw new OrmException("Cannot upgrade from " + curr.versionNbr - + "; manually run scripts from" - + " http://gerrit.googlecode.com/files/schema-upgrades003_019.zip" - + " and restart."); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_20.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_20.java deleted file mode 100644 index 4d8dca7..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_20.java +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright (C) 2009 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.schema; - -import com.google.inject.Inject; -import com.google.inject.Provider; - -class Schema_20 extends SchemaVersion { - @Inject - Schema_20(Provider<Schema_19> prior) { - super(prior); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_21.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_21.java deleted file mode 100644 index ed50f7f..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_21.java +++ /dev/null
@@ -1,74 +0,0 @@ -// Copyright (C) 2009 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.schema; - -import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gerrit.reviewdb.SystemConfig; -import com.google.gwtorm.client.OrmException; -import com.google.gwtorm.jdbc.JdbcSchema; -import com.google.gwtorm.schema.sql.DialectH2; -import com.google.gwtorm.schema.sql.DialectMySQL; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Collections; - -class Schema_21 extends SchemaVersion { - @Inject - Schema_21(Provider<Schema_20> prior) { - super(prior); - } - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException { - JdbcSchema jdbc = (JdbcSchema) db; - SystemConfig sc = db.systemConfig().get(new SystemConfig.Key()); - - Statement s = jdbc.getConnection().createStatement(); - try { - ResultSet r; - - r = s.executeQuery("SELECT name FROM projects WHERE project_id = 0"); - try { - if (!r.next()) { - throw new OrmException("Cannot read old wild project"); - } - sc.wildProjectName = new Project.NameKey(r.getString(1)); - } finally { - r.close(); - } - - if (jdbc.getDialect() instanceof DialectMySQL) { - try { - s.execute("DROP FUNCTION nextval_project_id"); - } catch (SQLException se) { - ui.message("warning: could not delete function nextval_project_id"); - } - - } else if (jdbc.getDialect() instanceof DialectH2) { - s.execute("ALTER TABLE projects DROP CONSTRAINT" - + " IF EXISTS CONSTRAINT_F3"); - } - } finally { - s.close(); - } - - db.systemConfig().update(Collections.singleton(sc)); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_22.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_22.java deleted file mode 100644 index 8e24aa2..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_22.java +++ /dev/null
@@ -1,74 +0,0 @@ -// Copyright (C) 2009 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.schema; - -import static com.google.gerrit.reviewdb.AccountExternalId.SCHEME_USERNAME; - -import com.google.gerrit.reviewdb.Account; -import com.google.gerrit.reviewdb.AccountExternalId; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gerrit.reviewdb.AccountExternalId.Key; -import com.google.gwtorm.client.OrmException; -import com.google.gwtorm.jdbc.JdbcSchema; -import com.google.gwtorm.schema.sql.DialectH2; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Collection; - -class Schema_22 extends SchemaVersion { - @Inject - Schema_22(Provider<Schema_21> prior) { - super(prior); - } - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException { - Statement s = ((JdbcSchema) db).getConnection().createStatement(); - try { - ResultSet results = - s.executeQuery(// - "SELECT account_id, ssh_user_name" - + " FROM accounts" // - + " WHERE ssh_user_name IS NOT NULL" - + " AND ssh_user_name <> ''"); - Collection<AccountExternalId> ids = new ArrayList<AccountExternalId>(); - while (results.next()) { - final int accountId = results.getInt(1); - final String userName = results.getString(2); - - final Account.Id account = new Account.Id(accountId); - final AccountExternalId.Key key = toKey(userName); - ids.add(new AccountExternalId(account, key)); - } - db.accountExternalIds().insert(ids); - - if (((JdbcSchema) db).getDialect() instanceof DialectH2) { - s.execute("ALTER TABLE accounts DROP CONSTRAINT" - + " IF EXISTS CONSTRAINT_AF"); - } - } finally { - s.close(); - } - } - - private Key toKey(final String userName) { - return new AccountExternalId.Key(SCHEME_USERNAME, userName); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_23.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_23.java deleted file mode 100644 index 413fa4e..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_23.java +++ /dev/null
@@ -1,61 +0,0 @@ -// Copyright (C) 2009 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.schema; - -import com.google.gerrit.reviewdb.AccountGroup; -import com.google.gerrit.reviewdb.AccountGroupName; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gwtorm.client.OrmException; -import com.google.gwtorm.jdbc.JdbcSchema; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Collection; - -class Schema_23 extends SchemaVersion { - @Inject - Schema_23(Provider<Schema_22> prior) { - super(prior); - } - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException { - Collection<AccountGroupName> names = new ArrayList<AccountGroupName>(); - Statement queryStmt = ((JdbcSchema) db).getConnection().createStatement(); - try { - ResultSet results = - queryStmt.executeQuery("SELECT group_id, name FROM account_groups"); - while (results.next()) { - final int id = results.getInt(1); - final String name = results.getString(2); - - final AccountGroup.Id group = new AccountGroup.Id(id); - final AccountGroup.NameKey key = toKey(name); - names.add(new AccountGroupName(key, group)); - } - } finally { - queryStmt.close(); - } - db.accountGroupNames().insert(names); - } - - private AccountGroup.NameKey toKey(final String name) { - return new AccountGroup.NameKey(name); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_24.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_24.java deleted file mode 100644 index 0172921..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_24.java +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.inject.Inject; -import com.google.inject.Provider; - -class Schema_24 extends SchemaVersion { - @Inject - Schema_24(Provider<Schema_23> prior) { - super(prior); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_25.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_25.java deleted file mode 100644 index fdfff70..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_25.java +++ /dev/null
@@ -1,112 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.gerrit.reviewdb.AccountGroup; -import com.google.gerrit.reviewdb.ApprovalCategory; -import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.RefRight; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gwtorm.client.OrmException; -import com.google.gwtorm.jdbc.JdbcSchema; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import org.eclipse.jgit.lib.Constants; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -class Schema_25 extends SchemaVersion { - private Set<ApprovalCategory.Id> nonActions; - - @Inject - Schema_25(Provider<Schema_24> prior) { - super(prior); - } - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException { - nonActions = new HashSet<ApprovalCategory.Id>(); - for (ApprovalCategory c : db.approvalCategories().all()) { - if (!c.isAction()) { - nonActions.add(c.getId()); - } - } - - List<RefRight> rights = new ArrayList<RefRight>(); - Statement stmt = ((JdbcSchema) db).getConnection().createStatement(); - try { - ResultSet rs = stmt.executeQuery("SELECT * FROM project_rights"); - try { - while (rs.next()) { - rights.add(toRefRight(rs)); - } - } finally { - rs.close(); - } - - db.refRights().insert(rights); - stmt.execute("CREATE INDEX ref_rights_byCatGroup" - + " ON ref_rights (category_id, group_id)"); - } finally { - stmt.close(); - } - } - - private RefRight toRefRight(ResultSet rs) throws SQLException { - short min_value = rs.getShort("min_value"); - short max_value = rs.getShort("max_value"); - String category_id = rs.getString("category_id"); - int group_id = rs.getInt("group_id"); - String project_name = rs.getString("project_name"); - - ApprovalCategory.Id category = new ApprovalCategory.Id(category_id); - Project.NameKey project = new Project.NameKey(project_name); - AccountGroup.Id group = new AccountGroup.Id(group_id); - - RefRight.RefPattern ref; - if (category.equals(ApprovalCategory.SUBMIT) - || category.equals(ApprovalCategory.PUSH_HEAD) - || nonActions.contains(category)) { - // Explicitly related to a branch head. - ref = new RefRight.RefPattern(Constants.R_HEADS + "*"); - - } else if (category.equals(ApprovalCategory.PUSH_TAG)) { - // Explicitly related to the tag namespace. - ref = new RefRight.RefPattern(Constants.R_TAGS + "/*"); - - } else if (category.equals(ApprovalCategory.READ) - || category.equals(ApprovalCategory.OWN)) { - // Currently these are project-wide rights, so apply that way. - ref = new RefRight.RefPattern(RefRight.ALL); - - } else { - // Assume project wide for the default. - ref = new RefRight.RefPattern(RefRight.ALL); - } - - RefRight.Key key = new RefRight.Key(project, ref, category, group); - RefRight r = new RefRight(key); - r.setMinValue(min_value); - r.setMaxValue(max_value); - return r; - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_26.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_26.java deleted file mode 100644 index 9c76af2..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_26.java +++ /dev/null
@@ -1,47 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gwtorm.jdbc.JdbcSchema; -import com.google.gwtorm.schema.sql.DialectMySQL; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.sql.SQLException; -import java.sql.Statement; - -class Schema_26 extends SchemaVersion { - @Inject - Schema_26(Provider<Schema_25> prior) { - super(prior); - } - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException { - if (((JdbcSchema) db).getDialect() instanceof DialectMySQL) { - Statement stmt = ((JdbcSchema) db).getConnection().createStatement(); - try { - stmt.execute("ALTER TABLE account_group_members_audit" // - + " MODIFY removed_on TIMESTAMP NULL DEFAULT NULL"); - stmt.execute("UPDATE account_group_members_audit" // - + " SET removed_on = NULL" // - + " WHERE removed_by IS NULL;"); - } finally { - stmt.close(); - } - } - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_27.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_27.java deleted file mode 100644 index f8febec..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_27.java +++ /dev/null
@@ -1,93 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.gerrit.reviewdb.AccountGroup; -import com.google.gerrit.reviewdb.AccountGroupName; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gwtorm.client.OrmException; -import com.google.gwtorm.jdbc.JdbcSchema; -import com.google.gwtorm.schema.sql.DialectH2; -import com.google.gwtorm.schema.sql.DialectMySQL; -import com.google.gwtorm.schema.sql.DialectPostgreSQL; -import com.google.gwtorm.schema.sql.SqlDialect; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -class Schema_27 extends SchemaVersion { - @Inject - Schema_27(Provider<Schema_26> prior) { - super(prior); - } - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException, OrmException { - Statement stmt = ((JdbcSchema) db).getConnection().createStatement(); - try { - final SqlDialect dialect = ((JdbcSchema) db).getDialect(); - if (dialect instanceof DialectPostgreSQL) { - stmt.execute("ALTER TABLE account_groups" - + " ALTER COLUMN name TYPE VARCHAR(255)"); - stmt.execute("ALTER TABLE account_group_names" - + " ALTER COLUMN name TYPE VARCHAR(255)"); - - } else if (dialect instanceof DialectH2) { - stmt.execute("ALTER TABLE account_groups" - + " ALTER COLUMN name VARCHAR(255)"); - stmt.execute("ALTER TABLE account_group_names" - + " ALTER COLUMN name VARCHAR(255) NOT NULL"); - - } else if (dialect instanceof DialectMySQL) { - stmt.execute("ALTER TABLE account_groups MODIFY name VARCHAR(255)"); - stmt.execute("ALTER TABLE account_group_names" - + " MODIFY name VARCHAR(255)"); - - } else { - throw new OrmException("Unsupported dialect " + dialect); - } - } finally { - stmt.close(); - } - - // Some groups might be missing their names, our older schema - // creation logic failed to create the name objects. Do it now. - // - Map<AccountGroup.NameKey, AccountGroupName> names = - db.accountGroupNames().toMap(db.accountGroupNames().all()); - - List<AccountGroupName> insert = new ArrayList<AccountGroupName>(); - List<AccountGroupName> update = new ArrayList<AccountGroupName>(); - - for (AccountGroup g : db.accountGroups().all()) { - AccountGroupName n = names.get(g.getNameKey()); - if (n == null) { - insert.add(new AccountGroupName(g)); - - } else if (!g.getId().equals(n.getId())) { - n.setId(g.getId()); - update.add(n); - } - } - - db.accountGroupNames().insert(insert); - db.accountGroupNames().update(update); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_28.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_28.java deleted file mode 100644 index cddbe4e..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_28.java +++ /dev/null
@@ -1,101 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.gerrit.reviewdb.ApprovalCategory; -import com.google.gerrit.reviewdb.ApprovalCategoryValue; -import com.google.gerrit.reviewdb.RefRight; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gerrit.reviewdb.SystemConfig; -import com.google.gerrit.server.workflow.NoOpFunction; -import com.google.gwtorm.client.OrmException; -import com.google.gwtorm.jdbc.JdbcSchema; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.Collections; - -class Schema_28 extends SchemaVersion { - @Inject - Schema_28(Provider<Schema_27> prior) { - super(prior); - } - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException { - final SystemConfig cfg = db.systemConfig().get(new SystemConfig.Key()); - ApprovalCategory cat; - - initForgeIdentityCategory(db, cfg); - - // Don't grant FORGE_COMMITTER to existing PUSH_HEAD rights. That - // is considered a bug that we are fixing with this schema upgrade. - // Administrators might need to relax permissions manually after the - // upgrade if that forgery is critical to their workflow. - - cat = db.approvalCategories().get(ApprovalCategory.PUSH_TAG); - if (cat != null && "Push Annotated Tag".equals(cat.getName())) { - cat.setName("Push Tag"); - db.approvalCategories().update(Collections.singleton(cat)); - } - - // Since we deleted Push Tags +3, drop anything using +3 down to +2. - // - Statement stmt = ((JdbcSchema) db).getConnection().createStatement(); - try { - stmt.execute("UPDATE ref_rights SET max_value = " - + ApprovalCategory.PUSH_TAG_ANNOTATED + " WHERE max_value >= 3"); - stmt.execute("UPDATE ref_rights SET min_value = " - + ApprovalCategory.PUSH_TAG_ANNOTATED + " WHERE min_value >= 3"); - } finally { - stmt.close(); - } - } - - private void initForgeIdentityCategory(final ReviewDb c, - final SystemConfig sConfig) throws OrmException { - final ApprovalCategory cat; - final ArrayList<ApprovalCategoryValue> values; - - cat = - new ApprovalCategory(ApprovalCategory.FORGE_IDENTITY, "Forge Identity"); - cat.setPosition((short) -1); - cat.setFunctionName(NoOpFunction.NAME); - values = new ArrayList<ApprovalCategoryValue>(); - values.add(value(cat, ApprovalCategory.FORGE_AUTHOR, - "Forge Author Identity")); - values.add(value(cat, ApprovalCategory.FORGE_COMMITTER, - "Forge Committer or Tagger Identity")); - c.approvalCategories().insert(Collections.singleton(cat)); - c.approvalCategoryValues().insert(values); - - RefRight right = - new RefRight(new RefRight.Key(sConfig.wildProjectName, - new RefRight.RefPattern(RefRight.ALL), - ApprovalCategory.FORGE_IDENTITY, sConfig.registeredGroupId)); - right.setMinValue(ApprovalCategory.FORGE_AUTHOR); - right.setMaxValue(ApprovalCategory.FORGE_AUTHOR); - c.refRights().insert(Collections.singleton(right)); - } - - private static ApprovalCategoryValue value(final ApprovalCategory cat, - final int value, final String name) { - return new ApprovalCategoryValue(new ApprovalCategoryValue.Id(cat.getId(), - (short) value), name); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_29.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_29.java deleted file mode 100644 index 37920bf..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_29.java +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.inject.Inject; -import com.google.inject.Provider; - -class Schema_29 extends SchemaVersion { - @Inject - Schema_29(Provider<Schema_28> prior) { - super(prior); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_30.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_30.java deleted file mode 100644 index 7285e32..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_30.java +++ /dev/null
@@ -1,40 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.gerrit.reviewdb.ApprovalCategory; -import com.google.gerrit.reviewdb.ApprovalCategoryValue; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gwtorm.client.OrmException; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.util.Collections; - -class Schema_30 extends SchemaVersion { - @Inject - Schema_30(Provider<Schema_29> prior) { - super(prior); - } - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException { - db.approvalCategoryValues().insert( - Collections.singleton(new ApprovalCategoryValue( - new ApprovalCategoryValue.Id(ApprovalCategory.FORGE_IDENTITY, - ApprovalCategory.FORGE_SERVER), - "Forge Gerrit Code Review Server Identity"))); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_31.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_31.java deleted file mode 100644 index 62f57e2..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_31.java +++ /dev/null
@@ -1,41 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gwtorm.jdbc.JdbcSchema; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.sql.SQLException; -import java.sql.Statement; - -class Schema_31 extends SchemaVersion { - @Inject - Schema_31(Provider<Schema_30> prior) { - super(prior); - } - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException { - Statement stmt = ((JdbcSchema) db).getConnection().createStatement(); - try { - stmt.execute("CREATE INDEX changes_byProject" - + " ON changes (dest_project_name)"); - } finally { - stmt.close(); - } - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_32.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_32.java deleted file mode 100644 index 2af5596..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_32.java +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.inject.Inject; -import com.google.inject.Provider; - -public class Schema_32 extends SchemaVersion { - @Inject - Schema_32(Provider<Schema_31> prior) { - super(prior); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_33.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_33.java deleted file mode 100644 index 0c733d7..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_33.java +++ /dev/null
@@ -1,49 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.gerrit.reviewdb.AccountGroup; -import com.google.gerrit.reviewdb.AccountGroupName; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gerrit.reviewdb.SystemConfig; -import com.google.gwtorm.client.OrmException; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.util.Collections; - -public class Schema_33 extends SchemaVersion { - @Inject - Schema_33(Provider<Schema_32> prior) { - super(prior); - } - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException { - SystemConfig config = db.systemConfig().all().toList().get(0); - final AccountGroup batchUsers = - new AccountGroup(new AccountGroup.NameKey("Non-Interactive Users"), - new AccountGroup.Id(db.nextAccountGroupId())); - batchUsers.setDescription("Users who perform batch actions on Gerrit"); - batchUsers.setOwnerGroupId(config.adminGroupId); - batchUsers.setType(AccountGroup.Type.INTERNAL); - db.accountGroups().insert(Collections.singleton(batchUsers)); - db.accountGroupNames().insert( - Collections.singleton(new AccountGroupName(batchUsers))); - - config.batchUsersGroupId = batchUsers.getId(); - db.systemConfig().update(Collections.singleton(config)); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_34.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_34.java deleted file mode 100644 index fa94146..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_34.java +++ /dev/null
@@ -1,123 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.gerrit.reviewdb.ApprovalCategory; -import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.RefRight; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gerrit.reviewdb.RefRight.RefPattern; -import com.google.gerrit.server.project.RefControl.RefRightsForPattern; -import com.google.gwtorm.client.OrmException; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -public class Schema_34 extends SchemaVersion { - private static final Comparator<String> DESCENDING_SORT = - new Comparator<String>() { - - @Override - public int compare(String a, String b) { - int aLength = a.length(); - int bLength = b.length(); - if (bLength == aLength) { - return a.compareTo(b); - } - return bLength - aLength; - } - }; - - @Inject - Schema_34(Provider<Schema_33> prior) { - super(prior); - } - - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException { - Iterable<Project> projects = db.projects().all(); - boolean showedBanner = false; - - List<RefRight> toUpdate = new ArrayList<RefRight>(); - List<RefRight> toDelete = new ArrayList<RefRight>(); - for (Project p : projects) { - boolean showedProject = false; - List<RefRight> pr = db.refRights().byProject(p.getNameKey()).toList(); - Map<ApprovalCategory.Id, Map<String, RefRightsForPattern>> r = - new HashMap<ApprovalCategory.Id, Map<String, RefRightsForPattern>>(); - for (RefRight right : pr) { - ApprovalCategory.Id cat = right.getApprovalCategoryId(); - if (r.get(cat) == null) { - Map<String, RefRightsForPattern> m = - new TreeMap<String, RefRightsForPattern>(DESCENDING_SORT); - r.put(cat, m); - } - if (r.get(cat).get(right.getRefPattern()) == null) { - RefRightsForPattern s = new RefRightsForPattern(); - r.get(cat).put(right.getRefPattern(), s); - } - r.get(cat).get(right.getRefPattern()).addRight(right); - } - - for (Map<String, RefRightsForPattern> categoryRights : r.values()) { - for (RefRightsForPattern rrp : categoryRights.values()) { - RefRight oldRight = rrp.getRights().get(0); - if (shouldPrompt(oldRight)) { - if (!showedBanner) { - ui.message("Entering interactive reference rights migration tool..."); - showedBanner = true; - } - if (!showedProject) { - ui.message("In project " + p.getName()); - showedProject = true; - } - ui.message("For category " + oldRight.getApprovalCategoryId()); - boolean isWildcard = oldRight.getRefPattern().endsWith("/*"); - boolean shouldUpdate = ui.yesno(!isWildcard, - "Should rights for pattern " - + oldRight.getRefPattern() - + " be considered exclusive?"); - if (shouldUpdate) { - RefRight.Key newKey = new RefRight.Key(oldRight.getProjectNameKey(), - new RefPattern("-" + oldRight.getRefPattern()), - oldRight.getApprovalCategoryId(), - oldRight.getAccountGroupId()); - RefRight newRight = new RefRight(newKey); - newRight.setMaxValue(oldRight.getMaxValue()); - newRight.setMinValue(oldRight.getMinValue()); - toUpdate.add(newRight); - toDelete.add(oldRight); - } - } - } - } - } - db.refRights().insert(toUpdate); - db.refRights().delete(toDelete); - } - - private boolean shouldPrompt(RefRight right) { - return !right.getRefPattern().equals("refs/*") - && !right.getRefPattern().equals("refs/heads/*") - && !right.getRefPattern().equals("refs/tags/*"); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_35.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_35.java deleted file mode 100644 index 12d90c3..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_35.java +++ /dev/null
@@ -1,41 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gwtorm.jdbc.JdbcSchema; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.sql.SQLException; -import java.sql.Statement; - -public class Schema_35 extends SchemaVersion { - @Inject - Schema_35(Provider<Schema_34> prior) { - super(prior); - } - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException { - Statement stmt = ((JdbcSchema) db).getConnection().createStatement(); - try { - stmt.execute("CREATE INDEX tracking_ids_byTrkId" - + " ON tracking_ids (tracking_id)"); - } finally { - stmt.close(); - } - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_36.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_36.java deleted file mode 100644 index ba6b841..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_36.java +++ /dev/null
@@ -1,51 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gwtorm.jdbc.JdbcSchema; -import com.google.gwtorm.schema.sql.DialectMySQL; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.sql.SQLException; -import java.sql.Statement; - -public class Schema_36 extends SchemaVersion { - @Inject - Schema_36(Provider<Schema_35> prior) { - super(prior); - } - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException { - Statement stmt = ((JdbcSchema) db).getConnection().createStatement(); - try { - if (((JdbcSchema) db).getDialect() instanceof DialectMySQL) { - stmt.execute("DROP INDEX account_project_watches_ntNew ON account_project_watches"); - stmt.execute("DROP INDEX account_project_watches_ntCmt ON account_project_watches"); - stmt.execute("DROP INDEX account_project_watches_ntSub ON account_project_watches"); - } else { - stmt.execute("DROP INDEX account_project_watches_ntNew"); - stmt.execute("DROP INDEX account_project_watches_ntCmt"); - stmt.execute("DROP INDEX account_project_watches_ntSub"); - } - stmt.execute("CREATE INDEX account_project_watches_byProject" - + " ON account_project_watches (project_name)"); - } finally { - stmt.close(); - } - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_37.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_37.java deleted file mode 100644 index 871f2e9..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_37.java +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.inject.Inject; -import com.google.inject.Provider; - -public class Schema_37 extends SchemaVersion { - @Inject - Schema_37(Provider<Schema_36> prior) { - super(prior); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_38.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_38.java deleted file mode 100644 index 59d6fa2..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_38.java +++ /dev/null
@@ -1,67 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.gerrit.reviewdb.Account; -import com.google.gerrit.reviewdb.AccountDiffPreference; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gwtorm.client.OrmException; -import com.google.gwtorm.jdbc.JdbcSchema; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.List; - -public class Schema_38 extends SchemaVersion { - @Inject - Schema_38(Provider<Schema_37> prior) { - super(prior); - } - - /** - * Migrate the account.default_context column to account_diff_preferences.context column. - * <p> - * Other fields in account_diff_preferences will be filled in with their defaults as - * defined in the {@link AccountDiffPreference#createDefault(com.google.gerrit.reviewdb.Account.Id)} - */ - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, - SQLException { - List<AccountDiffPreference> newPrefs = - new ArrayList<AccountDiffPreference>(); - - Statement stmt = ((JdbcSchema) db).getConnection().createStatement(); - try { - ResultSet result = - stmt.executeQuery("SELECT account_id, default_context" - + " FROM accounts WHERE default_context <> 10"); - while (result.next()) { - int accountId = result.getInt(1); - short defaultContext = result.getShort(2); - AccountDiffPreference diffPref = AccountDiffPreference.createDefault(new Account.Id(accountId)); - diffPref.setContext(defaultContext); - newPrefs.add(diffPref); - } - } finally { - stmt.close(); - } - - db.accountDiffPreferences().insert(newPrefs); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_39.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_39.java deleted file mode 100644 index 39ae226..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_39.java +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.inject.Inject; -import com.google.inject.Provider; - -public class Schema_39 extends SchemaVersion { - @Inject - Schema_39(Provider<Schema_38> prior) { - super(prior); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_40.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_40.java deleted file mode 100644 index 7d3e4f5..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_40.java +++ /dev/null
@@ -1,70 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.gerrit.reviewdb.AccountProjectWatch; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gwtorm.client.OrmException; -import com.google.gwtorm.jdbc.JdbcSchema; -import com.google.gwtorm.schema.sql.DialectH2; -import com.google.gwtorm.schema.sql.DialectMySQL; -import com.google.gwtorm.schema.sql.DialectPostgreSQL; -import com.google.gwtorm.schema.sql.SqlDialect; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.sql.SQLException; -import java.sql.Statement; - -public class Schema_40 extends SchemaVersion { - @Inject - Schema_40(Provider<Schema_39> prior) { - super(prior); - } - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException, - OrmException { - // Set to "*" the filter field of the previously watched projects - // - Statement stmt = ((JdbcSchema) db).getConnection().createStatement(); - try { - stmt.execute("UPDATE account_project_watches" // - + " SET filter = '" + AccountProjectWatch.FILTER_ALL + "'" // - + " WHERE filter IS NULL OR filter = ''"); - - // Set the new primary key - // - final SqlDialect dialect = ((JdbcSchema) db).getDialect(); - if (dialect instanceof DialectPostgreSQL) { - stmt.execute("ALTER TABLE account_project_watches " - + "DROP CONSTRAINT account_project_watches_pkey"); - stmt.execute("ALTER TABLE account_project_watches " - + "ADD PRIMARY KEY (account_id, project_name, filter)"); - - } else if ((dialect instanceof DialectH2) - || (dialect instanceof DialectMySQL)) { - stmt.execute("ALTER TABLE account_project_watches DROP PRIMARY KEY"); - stmt.execute("ALTER TABLE account_project_watches " - + "ADD PRIMARY KEY (account_id, project_name, filter)"); - - } else { - throw new OrmException("Unsupported dialect " + dialect); - } - } finally { - stmt.close(); - } - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_41.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_41.java deleted file mode 100644 index 508db43..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_41.java +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.inject.Inject; -import com.google.inject.Provider; - -public class Schema_41 extends SchemaVersion { - @Inject - Schema_41(Provider<Schema_40> prior) { - super(prior); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_42.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_42.java deleted file mode 100644 index 83bca7b..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_42.java +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.inject.Inject; -import com.google.inject.Provider; - -public class Schema_42 extends SchemaVersion { - @Inject - Schema_42(Provider<Schema_41> prior) { - super(prior); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_43.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_43.java deleted file mode 100644 index 0edb7e5..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_43.java +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.inject.Inject; -import com.google.inject.Provider; - -public class Schema_43 extends SchemaVersion { - @Inject - Schema_43(Provider<Schema_42> prior) { - super(prior); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_44.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_44.java deleted file mode 100644 index 4ab1986..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_44.java +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.inject.Inject; -import com.google.inject.Provider; - -public class Schema_44 extends SchemaVersion { - @Inject - Schema_44(Provider<Schema_43> prior) { - super(prior); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_45.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_45.java deleted file mode 100644 index e37e87d..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_45.java +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.inject.Inject; -import com.google.inject.Provider; - -public class Schema_45 extends SchemaVersion { - @Inject - Schema_45(Provider<Schema_44> prior) { - super(prior); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_46.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_46.java deleted file mode 100644 index e7b104c..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_46.java +++ /dev/null
@@ -1,67 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.gerrit.reviewdb.AccountGroup; -import com.google.gerrit.reviewdb.AccountGroupName; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gwtorm.client.OrmException; -import com.google.gwtorm.jdbc.JdbcSchema; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Collections; - -public class Schema_46 extends SchemaVersion { - - @Inject - Schema_46(final Provider<Schema_45> prior) { - super(prior); - } - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException, - OrmException { - AccountGroup.Id groupId = new AccountGroup.Id(db.nextAccountGroupId()); - - // update system_config - final Connection connection = ((JdbcSchema) db).getConnection(); - Statement stmt = null; - try { - stmt = connection.createStatement(); - stmt.execute("UPDATE system_config SET OWNER_GROUP_ID = " + groupId.get()); - final ResultSet resultSet = - stmt.executeQuery("SELECT ADMIN_GROUP_ID FROM system_config"); - resultSet.next(); - final int adminGroupId = resultSet.getInt(1); - - // create 'Project Owners' group - AccountGroup.NameKey nameKey = new AccountGroup.NameKey("Project Owners"); - AccountGroup group = new AccountGroup(nameKey, groupId); - group.setType(AccountGroup.Type.SYSTEM); - group.setOwnerGroupId(new AccountGroup.Id(adminGroupId)); - group.setDescription("Any owner of the project"); - AccountGroupName gn = new AccountGroupName(group); - db.accountGroupNames().insert(Collections.singleton(gn)); - db.accountGroups().insert(Collections.singleton(group)); - } finally { - if (stmt != null) stmt.close(); - } - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_47.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_47.java deleted file mode 100644 index 124cc02..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_47.java +++ /dev/null
@@ -1,25 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.inject.Inject; -import com.google.inject.Provider; - -public class Schema_47 extends SchemaVersion { - @Inject - Schema_47(Provider<Schema_46> prior) { - super(prior); - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_48.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_48.java deleted file mode 100644 index 4e8b94d..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_48.java +++ /dev/null
@@ -1,54 +0,0 @@ -// Copyright (C) 2010 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.schema; - -import com.google.gerrit.reviewdb.ApprovalCategory; -import com.google.gerrit.reviewdb.ApprovalCategoryValue; -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gwtorm.client.OrmException; -import com.google.gwtorm.jdbc.JdbcSchema; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.sql.SQLException; -import java.sql.Statement; - -import java.util.Collections; - -public class Schema_48 extends SchemaVersion { - @Inject - Schema_48(Provider<Schema_47> prior) { - super(prior); - } - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, SQLException { - // Read +3 allows merges to be uploaded - db.approvalCategoryValues().insert( - Collections.singleton(new ApprovalCategoryValue( - new ApprovalCategoryValue.Id(ApprovalCategory.READ, (short) 3), - "Upload merges permission"))); - // Since we added Read +3, elevate any Read +2 to that level to provide - // access equivalent to prior schema versions. - Statement stmt = ((JdbcSchema) db).getConnection().createStatement(); - try { - stmt.execute("UPDATE ref_rights SET max_value = 3" - + " WHERE category_id = '" + ApprovalCategory.READ.get() - + "' AND max_value = 2"); - } finally { - stmt.close(); - } - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_50.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_50.java deleted file mode 100644 index 4a90c1f..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_50.java +++ /dev/null
@@ -1,25 +0,0 @@ -// 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.schema; - -import com.google.inject.Inject; -import com.google.inject.Provider; - -public class Schema_50 extends SchemaVersion { - @Inject - Schema_50(Provider<Schema_49> prior) { - super(prior); - } -} \ No newline at end of file
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_51.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_51.java deleted file mode 100644 index d111160..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_51.java +++ /dev/null
@@ -1,41 +0,0 @@ -// 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.schema; - -import com.google.gerrit.reviewdb.ReviewDb; -import com.google.gwtorm.jdbc.JdbcSchema; -import com.google.inject.Inject; -import com.google.inject.Provider; - -import java.sql.SQLException; -import java.sql.Statement; - -public class Schema_51 extends SchemaVersion { - @Inject - Schema_51(Provider<Schema_50> prior) { - super(prior); - } - - @Override - protected void migrateData(ReviewDb db, UpdateUI ui) throws SQLException { - Statement stmt = ((JdbcSchema) db).getConnection().createStatement(); - try { - stmt.execute("CREATE INDEX account_group_includes_byInclude" - + " ON account_group_includes (include_id)"); - } finally { - stmt.close(); - } - } -}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_52.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_52.java index 3fbbbe0..e16b95c 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_52.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_52.java
@@ -14,12 +14,28 @@ package com.google.gerrit.server.schema; +import com.google.gerrit.reviewdb.CurrentSchemaVersion; +import com.google.gerrit.reviewdb.ReviewDb; +import com.google.gwtorm.client.OrmException; import com.google.inject.Inject; import com.google.inject.Provider; +import com.google.inject.ProvisionException; public class Schema_52 extends SchemaVersion { @Inject - Schema_52(Provider<Schema_51> prior) { - super(prior); + Schema_52() { + super(new Provider<SchemaVersion>() { + public SchemaVersion get() { + throw new ProvisionException("Cannot upgrade from 51"); + } + }); + } + + @Override + protected void upgradeFrom(UpdateUI ui, CurrentSchemaVersion curr, + ReviewDb db, boolean toTargetVersion) throws OrmException { + throw new OrmException("Cannot upgrade from schema " + curr.versionNbr + + "; manually run init from Gerrit Code Review 2.1.7" + + " and restart this version to continue."); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_53.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_53.java new file mode 100644 index 0000000..8a04071 --- /dev/null +++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/Schema_53.java
@@ -0,0 +1,473 @@ +// Copyright (C) 2010 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.schema; + +import static com.google.gerrit.common.data.Permission.CREATE; +import static com.google.gerrit.common.data.Permission.FORGE_AUTHOR; +import static com.google.gerrit.common.data.Permission.FORGE_COMMITTER; +import static com.google.gerrit.common.data.Permission.FORGE_SERVER; +import static com.google.gerrit.common.data.Permission.LABEL; +import static com.google.gerrit.common.data.Permission.OWNER; +import static com.google.gerrit.common.data.Permission.PUSH; +import static com.google.gerrit.common.data.Permission.PUSH_MERGE; +import static com.google.gerrit.common.data.Permission.PUSH_TAG; +import static com.google.gerrit.common.data.Permission.READ; +import static com.google.gerrit.common.data.Permission.SUBMIT; + +import com.google.gerrit.common.data.AccessSection; +import com.google.gerrit.common.data.GroupReference; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.common.data.PermissionRule; +import com.google.gerrit.reviewdb.AccountGroup; +import com.google.gerrit.reviewdb.ApprovalCategory; +import com.google.gerrit.reviewdb.Project; +import com.google.gerrit.reviewdb.ReviewDb; +import com.google.gerrit.reviewdb.SystemConfig; +import com.google.gerrit.server.GerritPersonIdent; +import com.google.gerrit.server.account.GroupUUID; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.git.MetaDataUpdate; +import com.google.gerrit.server.git.NoReplication; +import com.google.gerrit.server.git.ProjectConfig; +import com.google.gwtorm.client.OrmException; +import com.google.gwtorm.jdbc.JdbcSchema; +import com.google.inject.Inject; +import com.google.inject.Provider; + +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.errors.RepositoryNotFoundException; +import org.eclipse.jgit.lib.PersonIdent; +import org.eclipse.jgit.lib.Repository; + +import java.io.IOException; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class Schema_53 extends SchemaVersion { + private final GitRepositoryManager mgr; + private final PersonIdent serverUser; + + private SystemConfig systemConfig; + private Map<AccountGroup.Id, GroupReference> groupMap; + private Map<ApprovalCategory.Id, ApprovalCategory> categoryMap; + private GroupReference projectOwners; + + private Map<Project.NameKey, Project.NameKey> parentsByProject; + private Map<Project.NameKey, List<OldRefRight>> rightsByProject; + + private final String OLD_SUBMIT = "SUBM"; + private final String OLD_READ = "READ"; + private final String OLD_OWN = "OWN"; + private final String OLD_PUSH_TAG = "pTAG"; + private final String OLD_PUSH_HEAD = "pHD"; + private final String OLD_FORGE_IDENTITY = "FORG"; + + @Inject + Schema_53(Provider<Schema_52> prior, GitRepositoryManager mgr, + @GerritPersonIdent PersonIdent serverUser) { + super(prior); + this.mgr = mgr; + this.serverUser = serverUser; + } + + @Override + protected void migrateData(ReviewDb db, UpdateUI ui) throws OrmException, + SQLException { + systemConfig = db.systemConfig().get(new SystemConfig.Key()); + categoryMap = db.approvalCategories().toMap(db.approvalCategories().all()); + + assignGroupUUIDs(db); + readOldRefRights(db); + readProjectParents(db); + exportProjectConfig(db); + + deleteActionCategories(db); + } + + private void deleteActionCategories(ReviewDb db) throws OrmException { + List<ApprovalCategory> delete = new ArrayList<ApprovalCategory>(); + for (ApprovalCategory category : categoryMap.values()) { + if (category.getPosition() < 0) { + delete.add(category); + } + } + db.approvalCategories().delete(delete); + } + + private void assignGroupUUIDs(ReviewDb db) throws OrmException { + groupMap = new HashMap<AccountGroup.Id, GroupReference>(); + List<AccountGroup> groups = db.accountGroups().all().toList(); + for (AccountGroup g : groups) { + if (g.getId().equals(systemConfig.ownerGroupId)) { + g.setGroupUUID(AccountGroup.PROJECT_OWNERS); + projectOwners = GroupReference.forGroup(g); + + } else if (g.getId().equals(systemConfig.anonymousGroupId)) { + g.setGroupUUID(AccountGroup.ANONYMOUS_USERS); + + } else if (g.getId().equals(systemConfig.registeredGroupId)) { + g.setGroupUUID(AccountGroup.REGISTERED_USERS); + + } else { + g.setGroupUUID(GroupUUID.make(g.getName(), serverUser)); + } + groupMap.put(g.getId(), GroupReference.forGroup(g)); + } + db.accountGroups().update(groups); + + systemConfig.adminGroupUUID = toUUID(systemConfig.adminGroupId); + systemConfig.batchUsersGroupUUID = toUUID(systemConfig.batchUsersGroupId); + db.systemConfig().update(Collections.singleton(systemConfig)); + } + + private AccountGroup.UUID toUUID(AccountGroup.Id id) { + return groupMap.get(id).getUUID(); + } + + private void exportProjectConfig(ReviewDb db) throws OrmException, + SQLException { + Statement stmt = ((JdbcSchema) db).getConnection().createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM projects ORDER BY name"); + while (rs.next()) { + final String name = rs.getString("name"); + final Project.NameKey nameKey = new Project.NameKey(name); + + Repository git; + try { + git = mgr.openRepository(nameKey); + } catch (RepositoryNotFoundException notFound) { + // A repository may be missing if this project existed only to store + // inheritable permissions. For example '-- All Projects --'. + try { + git = mgr.createRepository(nameKey); + } catch (RepositoryNotFoundException err) { + throw new OrmException("Cannot create repository " + name, err); + } + } + try { + MetaDataUpdate md = + new MetaDataUpdate(new NoReplication(), nameKey, git); + md.getCommitBuilder().setAuthor(serverUser); + md.getCommitBuilder().setCommitter(serverUser); + + ProjectConfig config = ProjectConfig.read(md); + loadProject(rs, config.getProject()); + config.getAccessSections().clear(); + convertRights(config); + + // Grant out read on the config branch by default. + // + if (config.getProject().getNameKey().equals(systemConfig.wildProjectName)) { + AccessSection meta = config.getAccessSection(GitRepositoryManager.REF_CONFIG, true); + Permission read = meta.getPermission(READ, true); + read.getRule(config.resolve(projectOwners), true); + } + + md.setMessage("Import project configuration from SQL\n"); + if (!config.commit(md)) { + throw new OrmException("Cannot export project " + name); + } + } catch (ConfigInvalidException err) { + throw new OrmException("Cannot read project " + name, err); + } catch (IOException err) { + throw new OrmException("Cannot export project " + name, err); + } finally { + git.close(); + } + } + rs.close(); + stmt.close(); + } + + private void loadProject(ResultSet rs, Project project) throws SQLException, + OrmException { + project.setDescription(rs.getString("description")); + project.setUseContributorAgreements("Y".equals(rs + .getString("use_contributor_agreements"))); + + switch (rs.getString("submit_type").charAt(0)) { + case 'F': + project.setSubmitType(Project.SubmitType.FAST_FORWARD_ONLY); + break; + case 'M': + project.setSubmitType(Project.SubmitType.MERGE_IF_NECESSARY); + break; + case 'A': + project.setSubmitType(Project.SubmitType.MERGE_ALWAYS); + break; + case 'C': + project.setSubmitType(Project.SubmitType.CHERRY_PICK); + break; + default: + throw new OrmException("Unsupported submit_type=" + + rs.getString("submit_type") + " on project " + project.getName()); + } + + project.setUseSignedOffBy("Y".equals(rs.getString("use_signed_off_by"))); + project.setRequireChangeID("Y".equals(rs.getString("require_change_id"))); + project.setUseContentMerge("Y".equals(rs.getString("use_content_merge"))); + project.setParentName(rs.getString("parent_name")); + } + + private void readOldRefRights(ReviewDb db) throws SQLException { + rightsByProject = new HashMap<Project.NameKey, List<OldRefRight>>(); + + Statement stmt = ((JdbcSchema) db).getConnection().createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM ref_rights"); + while (rs.next()) { + OldRefRight right = new OldRefRight(rs); + if (right.group == null || right.category == null) { + continue; + } + + List<OldRefRight> list; + + list = rightsByProject.get(right.project); + if (list == null) { + list = new ArrayList<OldRefRight>(); + rightsByProject.put(right.project, list); + } + list.add(right); + } + rs.close(); + stmt.close(); + } + + private void readProjectParents(ReviewDb db) throws SQLException { + parentsByProject = new HashMap<Project.NameKey, Project.NameKey>(); + Statement stmt = ((JdbcSchema) db).getConnection().createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM projects"); + while (rs.next()) { + String name = rs.getString("name"); + String parent_name = rs.getString("parent_name"); + if (parent_name == null) { + parent_name = systemConfig.wildProjectName.get(); + } + parentsByProject.put(new Project.NameKey(name), // + new Project.NameKey(parent_name)); + } + rs.close(); + stmt.close(); + } + + private void convertRights(ProjectConfig config) { + List<OldRefRight> myRights = + rightsByProject.get(config.getProject().getNameKey()); + if (myRights == null) { + return; + } + + for (OldRefRight old : myRights) { + AccessSection section = config.getAccessSection(old.ref_pattern, true); + GroupReference group = config.resolve(old.group); + + if (OLD_SUBMIT.equals(old.category)) { + PermissionRule submit = rule(group); + submit.setDeny(old.max_value <= 0); + add(section, SUBMIT, old.exclusive, submit); + + } else if (OLD_READ.equals(old.category)) { + if (old.exclusive) { + section.getPermission(READ, true).setExclusiveGroup(true); + newChangePermission(config, old.ref_pattern).setExclusiveGroup(true); + } + + PermissionRule read = rule(group); + read.setDeny(old.max_value <= 0); + add(section, READ, old.exclusive, read); + + if (3 <= old.max_value) { + newMergePermission(config, old.ref_pattern).add(rule(group)); + } else if (3 <= inheritedMax(config, old)) { + newMergePermission(config, old.ref_pattern).add(deny(group)); + } + + if (2 <= old.max_value) { + newChangePermission(config, old.ref_pattern).add(rule(group)); + } else if (2 <= inheritedMax(config, old)) { + newChangePermission(config, old.ref_pattern).add(deny(group)); + } + + } else if (OLD_OWN.equals(old.category)) { + add(section, OWNER, false, rule(group)); + + } else if (OLD_PUSH_TAG.equals(old.category)) { + PermissionRule push = rule(group); + push.setDeny(old.max_value <= 0); + add(section, PUSH_TAG, old.exclusive, push); + + } else if (OLD_PUSH_HEAD.equals(old.category)) { + if (old.exclusive) { + section.getPermission(PUSH, true).setExclusiveGroup(true); + section.getPermission(CREATE, true).setExclusiveGroup(true); + } + + PermissionRule push = rule(group); + push.setDeny(old.max_value <= 0); + push.setForce(3 <= old.max_value); + add(section, PUSH, old.exclusive, push); + + if (2 <= old.max_value) { + add(section, CREATE, old.exclusive, rule(group)); + } else if (2 <= inheritedMax(config, old)) { + add(section, CREATE, old.exclusive, deny(group)); + } + + } else if (OLD_FORGE_IDENTITY.equals(old.category)) { + if (old.exclusive) { + section.getPermission(FORGE_AUTHOR, true).setExclusiveGroup(true); + section.getPermission(FORGE_COMMITTER, true).setExclusiveGroup(true); + section.getPermission(FORGE_SERVER, true).setExclusiveGroup(true); + } + + if (1 <= old.max_value) { + add(section, FORGE_AUTHOR, old.exclusive, rule(group)); + } + + if (2 <= old.max_value) { + add(section, FORGE_COMMITTER, old.exclusive, rule(group)); + } else if (2 <= inheritedMax(config, old)) { + add(section, FORGE_COMMITTER, old.exclusive, deny(group)); + } + + if (3 <= old.max_value) { + add(section, FORGE_SERVER, old.exclusive, rule(group)); + } else if (3 <= inheritedMax(config, old)) { + add(section, FORGE_SERVER, old.exclusive, deny(group)); + } + + } else { + PermissionRule rule = rule(group); + rule.setRange(old.min_value, old.max_value); + if (old.min_value == 0 && old.max_value == 0) { + rule.setDeny(true); + } + add(section, LABEL + varNameOf(old.category), old.exclusive, rule); + } + } + } + + private static Permission newChangePermission(ProjectConfig config, + String name) { + if (name.startsWith(AccessSection.REGEX_PREFIX)) { + name = AccessSection.REGEX_PREFIX + + "refs/for/" + + name.substring(AccessSection.REGEX_PREFIX.length()); + } else { + name = "refs/for/" + name; + } + return config.getAccessSection(name, true).getPermission(PUSH, true); + } + + private static Permission newMergePermission(ProjectConfig config, + String name) { + if (name.startsWith(AccessSection.REGEX_PREFIX)) { + name = AccessSection.REGEX_PREFIX + + "refs/for/" + + name.substring(AccessSection.REGEX_PREFIX.length()); + } else { + name = "refs/for/" + name; + } + return config.getAccessSection(name, true).getPermission(PUSH_MERGE, true); + } + + private static PermissionRule rule(GroupReference group) { + return new PermissionRule(group); + } + + private static PermissionRule deny(GroupReference group) { + PermissionRule rule = rule(group); + rule.setDeny(true); + return rule; + } + + private int inheritedMax(ProjectConfig config, OldRefRight old) { + int max = 0; + + String ref = old.ref_pattern; + String category = old.category; + AccountGroup.UUID group = old.group.getUUID(); + + Project.NameKey project = config.getProject().getParent(); + if (project == null) { + project = systemConfig.wildProjectName; + } + do { + List<OldRefRight> rights = rightsByProject.get(project); + if (rights != null) { + for (OldRefRight r : rights) { + if (r.ref_pattern.equals(ref) // + && r.group.getUUID().equals(group) // + && r.category.equals(category)) { + max = Math.max(max, r.max_value); + break; + } + } + } + project = parentsByProject.get(project); + } while (!project.equals(systemConfig.wildProjectName)); + + return max; + } + + private String varNameOf(String id) { + ApprovalCategory category = categoryMap.get(new ApprovalCategory.Id(id)); + if (category == null) { + category = new ApprovalCategory(new ApprovalCategory.Id(id), id); + } + return category.getLabelName(); + } + + private static void add(AccessSection section, String name, + boolean exclusive, PermissionRule rule) { + Permission p = section.getPermission(name, true); + if (exclusive) { + p.setExclusiveGroup(true); + } + p.add(rule); + } + + private class OldRefRight { + final int min_value; + final int max_value; + final String ref_pattern; + final boolean exclusive; + final GroupReference group; + final String category; + final Project.NameKey project; + + OldRefRight(ResultSet rs) throws SQLException { + min_value = rs.getInt("min_value"); + max_value = rs.getInt("max_value"); + project = new Project.NameKey(rs.getString("project_name")); + + String r = rs.getString("ref_pattern"); + exclusive = r.startsWith("-"); + if (exclusive) { + r = r.substring(1); + } + ref_pattern = r; + + category = rs.getString("category_id"); + group = groupMap.get(new AccountGroup.Id(rs.getInt("group_id"))); + } + } +}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/CategoryFunction.java b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/CategoryFunction.java index 67003932..ffed95a 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/CategoryFunction.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/CategoryFunction.java
@@ -15,11 +15,10 @@ package com.google.gerrit.server.workflow; import com.google.gerrit.common.data.ApprovalType; +import com.google.gerrit.common.data.Permission; import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.PatchSetApproval; -import com.google.gerrit.reviewdb.RefRight; import com.google.gerrit.server.CurrentUser; -import com.google.gerrit.server.project.RefControl; import java.util.HashMap; import java.util.Map; @@ -29,7 +28,6 @@ private static Map<String, CategoryFunction> all = new HashMap<String, CategoryFunction>(); static { - all.put(SubmitFunction.NAME, new SubmitFunction()); all.put(MaxWithBlock.NAME, new MaxWithBlock()); all.put(MaxNoBlock.NAME, new MaxNoBlock()); all.put(NoOpFunction.NAME, new NoOpFunction()); @@ -44,22 +42,11 @@ * is not known to Gerrit and thus cannot be executed. */ public static CategoryFunction forCategory(final ApprovalCategory category) { - final CategoryFunction r = forName(category.getFunctionName()); + final CategoryFunction r = all.get(category.getFunctionName()); return r != null ? r : new NoOpFunction(); } /** - * Locate a function by name. - * - * @param functionName the function's unique name. - * @return the function implementation; null if the function is not known to - * Gerrit and thus cannot be executed. - */ - public static CategoryFunction forName(final String functionName) { - return all.get(functionName); - } - - /** * Normalize ChangeApprovals and set the valid flag for this category. * <p> * Implementors should invoke: @@ -92,13 +79,8 @@ public boolean isValid(final CurrentUser user, final ApprovalType at, final FunctionState state) { - RefControl rc = state.controlFor(user); - for (final RefRight pr : rc.getApplicableRights(at.getCategory().getId())) { - if (user.getEffectiveGroups().contains(pr.getAccountGroupId()) - && (pr.getMinValue() < 0 || pr.getMaxValue() > 0)) { - return true; - } - } - return false; + return !state.controlFor(user) // + .getRange(Permission.forLabel(at.getCategory().getLabelName())) // + .isEmpty(); } }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java index 36a52e2..2cb3e81 100644 --- a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java +++ b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/FunctionState.java
@@ -16,13 +16,13 @@ import com.google.gerrit.common.data.ApprovalType; import com.google.gerrit.common.data.ApprovalTypes; -import com.google.gerrit.reviewdb.AccountGroup; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.common.data.PermissionRange; import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.ApprovalCategoryValue; import com.google.gerrit.reviewdb.Change; import com.google.gerrit.reviewdb.PatchSet; import com.google.gerrit.reviewdb.PatchSetApproval; -import com.google.gerrit.reviewdb.RefRight; import com.google.gerrit.reviewdb.ApprovalCategory.Id; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.IdentifiedUser; @@ -139,28 +139,12 @@ * of them is used. * <p> */ - private void applyRightFloor(final PatchSetApproval a) { + private void applyRightFloor(final ApprovalType at, final PatchSetApproval a) { + final ApprovalCategory category = at.getCategory(); + final String permission = Permission.forLabel(category.getLabelName()); final IdentifiedUser user = userFactory.create(a.getAccountId()); - RefControl rc = controlFor(user); - - // Find the maximal range actually granted to the user. - // - short minAllowed = 0, maxAllowed = 0; - for (final RefRight r : rc.getApplicableRights(a.getCategoryId())) { - final AccountGroup.Id grp = r.getAccountGroupId(); - if (user.getEffectiveGroups().contains(grp)) { - minAllowed = (short) Math.min(minAllowed, r.getMinValue()); - maxAllowed = (short) Math.max(maxAllowed, r.getMaxValue()); - } - } - - // Normalize the value into that range. - // - if (a.getValue() < minAllowed) { - a.setValue(minAllowed); - } else if (a.getValue() > maxAllowed) { - a.setValue(maxAllowed); - } + final PermissionRange range = controlFor(user).getRange(permission); + a.setValue((short) range.squash(a.getValue())); } RefControl controlFor(final CurrentUser user) { @@ -172,7 +156,7 @@ /** Run <code>applyTypeFloor</code>, <code>applyRightFloor</code>. */ public void normalize(final ApprovalType at, final PatchSetApproval ca) { applyTypeFloor(at, ca); - applyRightFloor(ca); + applyRightFloor(at, ca); } private static Id id(final ApprovalType at) {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/SubmitFunction.java b/gerrit-server/src/main/java/com/google/gerrit/server/workflow/SubmitFunction.java deleted file mode 100644 index f0a00ff..0000000 --- a/gerrit-server/src/main/java/com/google/gerrit/server/workflow/SubmitFunction.java +++ /dev/null
@@ -1,68 +0,0 @@ -// Copyright (C) 2008 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.gerrit.server.workflow; - -import com.google.gerrit.common.data.ApprovalType; -import com.google.gerrit.reviewdb.ApprovalCategory; -import com.google.gerrit.reviewdb.Change; -import com.google.gerrit.reviewdb.RefRight; -import com.google.gerrit.server.CurrentUser; -import com.google.gerrit.server.project.RefControl; - -/** - * Computes if the submit function can be used. - * <p> - * In order to be considered "approved" this function requires that all approval - * categories with a position >= 0 (that is any whose - * {@link ApprovalCategory#isAction()} method returns false) is valid and that - * the change state be {@link Change.Status#NEW}. - * <p> - * This is mostly useful for actions, like {@link ApprovalCategory#SUBMIT}. - */ -public class SubmitFunction extends CategoryFunction { - public static String NAME = "Submit"; - - @Override - public void run(final ApprovalType at, final FunctionState state) { - state.valid(at, valid(at, state)); - } - - @Override - public boolean isValid(final CurrentUser user, final ApprovalType at, - final FunctionState state) { - if (valid(at, state)) { - RefControl rc = state.controlFor(user); - for (final RefRight pr : rc.getApplicableRights(at.getCategory().getId())) { - if (user.getEffectiveGroups().contains(pr.getAccountGroupId()) - && pr.getMaxValue() > 0) { - return true; - } - } - } - return false; - } - - private static boolean valid(final ApprovalType at, final FunctionState state) { - if (state.getChange().getStatus() != Change.Status.NEW) { - return false; - } - for (final ApprovalType t : state.getApprovalTypes()) { - if (!state.isValid(t)) { - return false; - } - } - return true; - } -}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java new file mode 100644 index 0000000..e8a235f --- /dev/null +++ b/gerrit-server/src/test/java/com/google/gerrit/server/git/ProjectConfigTest.java
@@ -0,0 +1,191 @@ +// Copyright (C) 2011 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.gerrit.server.git; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.gerrit.common.data.AccessSection; +import com.google.gerrit.common.data.GroupReference; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.common.data.PermissionRule; +import com.google.gerrit.reviewdb.AccountGroup; +import com.google.gerrit.reviewdb.Project; + +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.errors.IncorrectObjectTypeException; +import org.eclipse.jgit.errors.MissingObjectException; +import org.eclipse.jgit.junit.LocalDiskRepositoryTestCase; +import org.eclipse.jgit.junit.TestRepository; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.RefUpdate; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.revwalk.RevCommit; +import org.eclipse.jgit.revwalk.RevObject; +import org.eclipse.jgit.util.RawParseUtils; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; + +public class ProjectConfigTest extends LocalDiskRepositoryTestCase { + private final GroupReference developers = new GroupReference( + new AccountGroup.UUID("X"), "Developers"); + private final GroupReference staff = new GroupReference( + new AccountGroup.UUID("Y"), "Staff"); + + private Repository db; + private TestRepository<Repository> util; + + @Override + @Before + public void setUp() throws Exception { + super.setUp(); + db = createBareRepository(); + util = new TestRepository<Repository>(db); + } + + @Test + public void testReadConfig() throws Exception { + RevCommit rev = util.commit(util.tree( // + util.file("groups", util.blob(group(developers))), // + util.file("project.config", util.blob(""// + + "[access \"refs/heads/*\"]\n" // + + " exclusiveGroupPermissions = read submit create\n" // + + " submit = group Developers\n" // + + " push = group Developers\n" // + + " read = group Developers\n")) // + )); + + ProjectConfig cfg = read(rev); + AccessSection section = cfg.getAccessSection("refs/heads/*"); + assertNotNull("has refs/heads/*", section); + assertNull("no refs/*", cfg.getAccessSection("refs/*")); + + Permission create = section.getPermission(Permission.CREATE); + Permission submit = section.getPermission(Permission.SUBMIT); + Permission read = section.getPermission(Permission.READ); + Permission push = section.getPermission(Permission.PUSH); + + assertTrue(create.getExclusiveGroup()); + assertTrue(submit.getExclusiveGroup()); + assertTrue(read.getExclusiveGroup()); + assertFalse(push.getExclusiveGroup()); + } + + @Test + public void testEditConfig() throws Exception { + RevCommit rev = util.commit(util.tree( // + util.file("groups", util.blob(group(developers))), // + util.file("project.config", util.blob(""// + + "[access \"refs/heads/*\"]\n" // + + " exclusiveGroupPermissions = read submit\n" // + + " submit = group Developers\n" // + + " upload = group Developers\n" // + + " read = group Developers\n")) // + )); + update(rev); + + ProjectConfig cfg = read(rev); + AccessSection section = cfg.getAccessSection("refs/heads/*"); + Permission submit = section.getPermission(Permission.SUBMIT); + submit.add(new PermissionRule(cfg.resolve(staff))); + rev = commit(cfg); + assertEquals(""// + + "[access \"refs/heads/*\"]\n" // + + " exclusiveGroupPermissions = read submit\n" // + + " submit = group Developers\n" // + + "\tsubmit = group Staff\n" // + + " upload = group Developers\n" // + + " read = group Developers\n", text(rev, "project.config")); + } + + @Test + public void testEditConfigMissingGroupTableEntry() throws Exception { + RevCommit rev = util.commit(util.tree( // + util.file("groups", util.blob(group(developers))), // + util.file("project.config", util.blob(""// + + "[access \"refs/heads/*\"]\n" // + + " exclusiveGroupPermissions = read submit\n" // + + " submit = group People Who Can Submit\n" // + + " upload = group Developers\n" // + + " read = group Developers\n")) // + )); + update(rev); + + ProjectConfig cfg = read(rev); + AccessSection section = cfg.getAccessSection("refs/heads/*"); + Permission submit = section.getPermission(Permission.SUBMIT); + submit.add(new PermissionRule(cfg.resolve(staff))); + rev = commit(cfg); + assertEquals(""// + + "[access \"refs/heads/*\"]\n" // + + " exclusiveGroupPermissions = read submit\n" // + + " submit = group People Who Can Submit\n" // + + "\tsubmit = group Staff\n" // + + " upload = group Developers\n" // + + " read = group Developers\n", text(rev, "project.config")); + } + + private ProjectConfig read(RevCommit rev) throws IOException, + ConfigInvalidException { + ProjectConfig cfg = new ProjectConfig(new Project.NameKey("test")); + cfg.load(db, rev); + return cfg; + } + + private RevCommit commit(ProjectConfig cfg) throws IOException, + MissingObjectException, IncorrectObjectTypeException { + MetaDataUpdate md = new MetaDataUpdate(new NoReplication(), // + cfg.getProject().getNameKey(), // + db); + util.tick(5); + util.setAuthorAndCommitter(md.getCommitBuilder()); + md.setMessage("Edit\n"); + assertTrue("commit finished", cfg.commit(md)); + + Ref ref = db.getRef(GitRepositoryManager.REF_CONFIG); + return util.getRevWalk().parseCommit(ref.getObjectId()); + } + + private void update(RevCommit rev) throws Exception { + RefUpdate u = db.updateRef(GitRepositoryManager.REF_CONFIG); + u.disableRefLog(); + u.setNewObjectId(rev); + switch (u.forceUpdate()) { + case FAST_FORWARD: + case FORCED: + case NEW: + case NO_CHANGE: + break; + default: + fail("Cannot update ref for test: " + u.getResult()); + } + } + + private String text(RevCommit rev, String path) throws Exception { + RevObject blob = util.get(rev.getTree(), path); + byte[] data = db.open(blob).getCachedBytes(Integer.MAX_VALUE); + return RawParseUtils.decode(data); + } + + private static String group(GroupReference g) { + return g.getUUID().get() + "\t" + g.getName() + "\n"; + } +}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java index f819dac..6d56b4a 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/mail/FromAddressGeneratorProviderTest.java
@@ -278,7 +278,7 @@ account.setFullName(name); account.setPreferredEmail(email); final AccountState s = - new AccountState(account, Collections.<AccountGroup.Id> emptySet(), + new AccountState(account, Collections.<AccountGroup.UUID> emptySet(), Collections.<AccountExternalId> emptySet()); return s; }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java index cf5d264..a037e30 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
@@ -14,23 +14,24 @@ package com.google.gerrit.server.project; -import static com.google.gerrit.reviewdb.ApprovalCategory.OWN; -import static com.google.gerrit.reviewdb.ApprovalCategory.READ; -import static com.google.gerrit.reviewdb.ApprovalCategory.SUBMIT; +import static com.google.gerrit.common.data.Permission.OWNER; +import static com.google.gerrit.common.data.Permission.PUSH; +import static com.google.gerrit.common.data.Permission.READ; +import static com.google.gerrit.common.data.Permission.SUBMIT; +import com.google.gerrit.common.data.GroupReference; +import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.reviewdb.AccountGroup; import com.google.gerrit.reviewdb.AccountProjectWatch; -import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.Change; import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.RefRight; import com.google.gerrit.reviewdb.SystemConfig; -import com.google.gerrit.reviewdb.RefRight.RefPattern; import com.google.gerrit.server.AccessPath; import com.google.gerrit.server.AnonymousUser; import com.google.gerrit.server.CurrentUser; import com.google.gerrit.server.config.AuthConfig; import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.git.ProjectConfig; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; @@ -41,17 +42,17 @@ import org.eclipse.jgit.lib.Config; import java.io.UnsupportedEncodingException; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; -import java.util.List; +import java.util.Map; import java.util.Set; public class RefControlTest extends TestCase { public void testOwnerProject() { - grant(local, OWN, admin, "refs/*", 1); + grant(local, OWNER, admin, "refs/*"); ProjectControl uBlah = user(devs); ProjectControl uAdmin = user(devs, admin); @@ -61,8 +62,8 @@ } public void testBranchDelegation1() { - grant(local, OWN, admin, "refs/*", 1); - grant(local, OWN, devs, "refs/heads/x/*", 1); + grant(local, OWNER, admin, "refs/*"); + grant(local, OWNER, devs, "refs/heads/x/*"); ProjectControl uDev = user(devs); assertFalse("not owner", uDev.isOwner()); @@ -77,9 +78,10 @@ } public void testBranchDelegation2() { - grant(local, OWN, admin, "refs/*", 1); - grant(local, OWN, devs, "refs/heads/x/*", 1); - grant(local, OWN, fixers, "-refs/heads/x/y/*", 1); + grant(local, OWNER, admin, "refs/*"); + grant(local, OWNER, devs, "refs/heads/x/*"); + grant(local, OWNER, fixers, "refs/heads/x/y/*"); + doNotInherit(local, OWNER, "refs/heads/x/y/*"); ProjectControl uDev = user(devs); assertFalse("not owner", uDev.isOwner()); @@ -104,8 +106,11 @@ } public void testInheritRead_SingleBranchDeniesUpload() { - grant(parent, READ, registered, "refs/*", 1, 2); - grant(local, READ, registered, "-refs/heads/foobar", 1); + grant(parent, READ, registered, "refs/*"); + grant(parent, PUSH, registered, "refs/for/refs/*"); + grant(local, READ, registered, "refs/heads/foobar"); + doNotInherit(local, READ, "refs/heads/foobar"); + doNotInherit(local, PUSH, "refs/for/refs/heads/foobar"); ProjectControl u = user(); assertTrue("can upload", u.canPushToAtLeastOneRef()); @@ -118,8 +123,9 @@ } public void testInheritRead_SingleBranchDoesNotOverrideInherited() { - grant(parent, READ, registered, "refs/*", 1, 2); - grant(local, READ, registered, "refs/heads/foobar", 1); + grant(parent, READ, registered, "refs/*"); + grant(parent, PUSH, registered, "refs/for/refs/*"); + grant(local, READ, registered, "refs/heads/foobar"); ProjectControl u = user(); assertTrue("can upload", u.canPushToAtLeastOneRef()); @@ -132,16 +138,16 @@ } public void testInheritRead_OverrideWithDeny() { - grant(parent, READ, registered, "refs/*", 1); - grant(local, READ, registered, "refs/*", 0); + grant(parent, READ, registered, "refs/*"); + grant(local, READ, registered, "refs/*").setDeny(true); ProjectControl u = user(); assertFalse("can't read", u.isVisible()); } public void testInheritRead_AppendWithDenyOfRef() { - grant(parent, READ, registered, "refs/*", 1); - grant(local, READ, registered, "refs/heads/*", 0); + grant(parent, READ, registered, "refs/*"); + grant(local, READ, registered, "refs/heads/*").setDeny(true); ProjectControl u = user(); assertTrue("can read", u.isVisible()); @@ -151,9 +157,9 @@ } public void testInheritRead_OverridesAndDeniesOfRef() { - grant(parent, READ, registered, "refs/*", 1); - grant(local, READ, registered, "refs/*", 0); - grant(local, READ, registered, "refs/heads/*", -1, 1); + grant(parent, READ, registered, "refs/*"); + grant(local, READ, registered, "refs/*").setDeny(true); + grant(local, READ, registered, "refs/heads/*"); ProjectControl u = user(); assertTrue("can read", u.isVisible()); @@ -163,9 +169,9 @@ } public void testInheritSubmit_OverridesAndDeniesOfRef() { - grant(parent, SUBMIT, registered, "refs/*", 1); - grant(local, SUBMIT, registered, "refs/*", 0); - grant(local, SUBMIT, registered, "refs/heads/*", -1, 1); + grant(parent, SUBMIT, registered, "refs/*"); + grant(local, SUBMIT, registered, "refs/*").setDeny(true); + grant(local, SUBMIT, registered, "refs/heads/*"); ProjectControl u = user(); assertFalse("can't submit", u.controlForRef("refs/foobar").canSubmit()); @@ -174,8 +180,9 @@ } public void testCannotUploadToAnyRef() { - grant(parent, READ, registered, "refs/*", 1); - grant(local, READ, devs, "refs/heads/*", 1, 2); + grant(parent, READ, registered, "refs/*"); + grant(local, READ, devs, "refs/heads/*"); + grant(local, PUSH, devs, "refs/for/refs/heads/*"); ProjectControl u = user(); assertFalse("cannot upload", u.canPushToAtLeastOneRef()); @@ -186,15 +193,14 @@ // ----------------------------------------------------------------------- - private final Project.NameKey local = new Project.NameKey("test"); - private final Project.NameKey parent = new Project.NameKey("parent"); - private final AccountGroup.Id admin = new AccountGroup.Id(1); - private final AccountGroup.Id anonymous = new AccountGroup.Id(2); - private final AccountGroup.Id registered = new AccountGroup.Id(3); - private final AccountGroup.Id owners = new AccountGroup.Id(4); + private ProjectConfig local; + private ProjectConfig parent; + private final AccountGroup.UUID admin = new AccountGroup.UUID("test.admin"); + private final AccountGroup.UUID anonymous = AccountGroup.ANONYMOUS_USERS; + private final AccountGroup.UUID registered = AccountGroup.REGISTERED_USERS; - private final AccountGroup.Id devs = new AccountGroup.Id(5); - private final AccountGroup.Id fixers = new AccountGroup.Id(6); + private final AccountGroup.UUID devs = new AccountGroup.UUID("test.devs"); + private final AccountGroup.UUID fixers = new AccountGroup.UUID("test.fixers"); private final SystemConfig systemConfig; private final AuthConfig authConfig; @@ -202,11 +208,8 @@ public RefControlTest() { systemConfig = SystemConfig.create(); - systemConfig.adminGroupId = admin; - systemConfig.anonymousGroupId = anonymous; - systemConfig.registeredGroupId = registered; - systemConfig.ownerGroupId = owners; - systemConfig.batchUsersGroupId = anonymous; + systemConfig.adminGroupUUID = admin; + systemConfig.batchUsersGroupUUID = anonymous; try { byte[] bin = "abcdefghijklmnopqrstuvwxyz".getBytes("UTF-8"); systemConfig.registerEmailPrivateKey = Base64.encodeBase64String(bin); @@ -230,14 +233,16 @@ anonymousUser = injector.getInstance(AnonymousUser.class); } - private List<RefRight> localRights; - private List<RefRight> inheritedRights; - @Override - protected void setUp() throws Exception { + public void setUp() throws Exception { super.setUp(); - localRights = new ArrayList<RefRight>(); - inheritedRights = new ArrayList<RefRight>(); + + parent = new ProjectConfig(new Project.NameKey("parent")); + parent.createInMemory(); + + local = new ProjectConfig(new Project.NameKey("local")); + local.createInMemory(); + local.getProject().setParentName(parent.getProject().getName()); } private static void assertOwner(String ref, ProjectControl u) { @@ -248,63 +253,90 @@ assertFalse("NOT OWN " + ref, u.controlForRef(ref).isOwner()); } - private void grant(Project.NameKey project, ApprovalCategory.Id categoryId, - AccountGroup.Id group, String ref, int maxValue) { - grant(project, categoryId, group, ref, maxValue, maxValue); + private PermissionRule grant(ProjectConfig project, String permissionName, + AccountGroup.UUID group, String ref) { + PermissionRule rule = newRule(project, group); + project.getAccessSection(ref, true) // + .getPermission(permissionName, true) // + .add(rule); + return rule; } - private void grant(Project.NameKey project, ApprovalCategory.Id categoryId, AccountGroup.Id group, - String ref, int minValue, int maxValue) { - RefRight right = - new RefRight(new RefRight.Key(project, new RefPattern(ref), - categoryId, group)); - right.setMinValue((short) minValue); - right.setMaxValue((short) maxValue); - - if (project == parent) { - inheritedRights.add(right); - } else if (project == local) { - localRights.add(right); - } else { - fail("Unknown project key: " + project); - } + private void doNotInherit(ProjectConfig project, String permissionName, + String ref) { + project.getAccessSection(ref, true) // + .getPermission(permissionName, true) // + .setExclusiveGroup(true); } - private ProjectControl user(AccountGroup.Id... memberOf) { + private PermissionRule newRule(ProjectConfig project, AccountGroup.UUID groupUUID) { + GroupReference group = new GroupReference(groupUUID, groupUUID.get()); + group = project.resolve(group); + + return new PermissionRule(group); + } + + private ProjectControl user(AccountGroup.UUID... memberOf) { RefControl.Factory refControlFactory = new RefControl.Factory() { @Override public RefControl create(final ProjectControl projectControl, final String ref) { - return new RefControl(systemConfig, projectControl, ref); + return new RefControl(projectControl, ref); } }; - return new ProjectControl(Collections.<AccountGroup.Id> emptySet(), - Collections.<AccountGroup.Id> emptySet(), refControlFactory, + return new ProjectControl(Collections.<AccountGroup.UUID> emptySet(), + Collections.<AccountGroup.UUID> emptySet(), refControlFactory, new MockUser(memberOf), newProjectState()); } private ProjectState newProjectState() { - ProjectCache projectCache = null; + final Map<Project.NameKey, ProjectState> all = + new HashMap<Project.NameKey, ProjectState>(); + final ProjectCache projectCache = new ProjectCache() { + @Override + public ProjectState get(Project.NameKey projectName) { + return all.get(projectName); + } + + @Override + public void evict(Project p) { + } + + @Override + public Iterable<Project.NameKey> all() { + return Collections.emptySet(); + } + + @Override + public Iterable<Project.NameKey> byName(String prefix) { + return Collections.emptySet(); + } + + @Override + public void onCreateProject(Project.NameKey newProjectName) { + } + }; + Project.NameKey wildProject = new Project.NameKey("-- All Projects --"); ProjectControl.AssistedFactory projectControlFactory = null; - ProjectState ps = - new ProjectState(anonymousUser, projectCache, wildProject, - projectControlFactory, new Project(parent), localRights); - ps.setInheritedRights(inheritedRights); - return ps; + all.put(local.getProject().getNameKey(), new ProjectState(anonymousUser, + projectCache, wildProject, projectControlFactory, local)); + all.put(parent.getProject().getNameKey(), new ProjectState(anonymousUser, + projectCache, wildProject, projectControlFactory, parent)); + return all.get(local.getProject().getNameKey()); } private class MockUser extends CurrentUser { - private final Set<AccountGroup.Id> groups; + private final Set<AccountGroup.UUID> groups; - MockUser(AccountGroup.Id[] groupId) { + MockUser(AccountGroup.UUID[] groupId) { super(AccessPath.UNKNOWN, RefControlTest.this.authConfig); - groups = new HashSet<AccountGroup.Id>(Arrays.asList(groupId)); + groups = new HashSet<AccountGroup.UUID>(Arrays.asList(groupId)); groups.add(registered); groups.add(anonymous); } @Override - public Set<AccountGroup.Id> getEffectiveGroups() { + public Set<AccountGroup.UUID> getEffectiveGroups() { return groups; }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java index ee9af13..1d70d4e 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaCreatorTest.java
@@ -17,12 +17,8 @@ import com.google.gerrit.reviewdb.AccountGroup; import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.ApprovalCategoryValue; -import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.RefRight; import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.reviewdb.SystemConfig; -import com.google.gerrit.server.workflow.NoOpFunction; -import com.google.gerrit.server.workflow.SubmitFunction; import com.google.gerrit.testutil.InMemoryDatabase; import com.google.gwtorm.client.OrmException; import com.google.gwtorm.jdbc.JdbcSchema; @@ -151,24 +147,6 @@ } } - public void testCreateSchema_WildCardProject() throws OrmException { - final ReviewDb c = db.create().open(); - try { - final SystemConfig cfg; - final Project all; - - cfg = c.systemConfig().get(new SystemConfig.Key()); - all = c.projects().get(cfg.wildProjectName); - assertNotNull(all); - assertEquals("-- All Projects --", all.getName()); - assertFalse(all.isUseContributorAgreements()); - assertFalse(all.isUseSignedOffBy()); - assertFalse(all.isRequireChangeID()); - } finally { - c.close(); - } - } - public void testCreateSchema_ApprovalCategory_CodeReview() throws OrmException { final ReviewDb c = db.create().open(); @@ -182,7 +160,6 @@ assertEquals("R", cat.getAbbreviatedName()); assertEquals("MaxWithBlock", cat.getFunctionName()); assertTrue(cat.isCopyMinScore()); - assertFalse(cat.isAction()); assertTrue(0 <= cat.getPosition()); } finally { c.close(); @@ -190,101 +167,6 @@ assertValueRange(codeReview, -2, -1, 0, 1, 2); } - public void testCreateSchema_ApprovalCategory_Read() throws OrmException { - final ReviewDb c = db.create().open(); - try { - final ApprovalCategory cat; - - cat = c.approvalCategories().get(ApprovalCategory.READ); - assertNotNull(cat); - assertEquals(ApprovalCategory.READ, cat.getId()); - assertEquals("Read Access", cat.getName()); - assertNull(cat.getAbbreviatedName()); - assertEquals(NoOpFunction.NAME, cat.getFunctionName()); - assertTrue(cat.isAction()); - } finally { - c.close(); - } - assertValueRange(ApprovalCategory.READ, -1, 1, 2, 3); - } - - public void testCreateSchema_ApprovalCategory_Submit() throws OrmException { - final ReviewDb c = db.create().open(); - try { - final ApprovalCategory cat; - - cat = c.approvalCategories().get(ApprovalCategory.SUBMIT); - assertNotNull(cat); - assertEquals(ApprovalCategory.SUBMIT, cat.getId()); - assertEquals("Submit", cat.getName()); - assertNull(cat.getAbbreviatedName()); - assertEquals(SubmitFunction.NAME, cat.getFunctionName()); - assertTrue(cat.isAction()); - } finally { - c.close(); - } - assertValueRange(ApprovalCategory.SUBMIT, 1); - } - - public void testCreateSchema_ApprovalCategory_PushTag() throws OrmException { - final ReviewDb c = db.create().open(); - try { - final ApprovalCategory cat; - - cat = c.approvalCategories().get(ApprovalCategory.PUSH_TAG); - assertNotNull(cat); - assertEquals(ApprovalCategory.PUSH_TAG, cat.getId()); - assertEquals("Push Tag", cat.getName()); - assertNull(cat.getAbbreviatedName()); - assertEquals(NoOpFunction.NAME, cat.getFunctionName()); - assertTrue(cat.isAction()); - } finally { - c.close(); - } - assertValueRange(ApprovalCategory.PUSH_TAG, // - ApprovalCategory.PUSH_TAG_SIGNED, // - ApprovalCategory.PUSH_TAG_ANNOTATED); - } - - public void testCreateSchema_ApprovalCategory_PushHead() throws OrmException { - final ReviewDb c = db.create().open(); - try { - final ApprovalCategory cat; - - cat = c.approvalCategories().get(ApprovalCategory.PUSH_HEAD); - assertNotNull(cat); - assertEquals(ApprovalCategory.PUSH_HEAD, cat.getId()); - assertEquals("Push Branch", cat.getName()); - assertNull(cat.getAbbreviatedName()); - assertEquals(NoOpFunction.NAME, cat.getFunctionName()); - assertTrue(cat.isAction()); - } finally { - c.close(); - } - assertValueRange(ApprovalCategory.PUSH_HEAD, // - ApprovalCategory.PUSH_HEAD_UPDATE, // - ApprovalCategory.PUSH_HEAD_CREATE, // - ApprovalCategory.PUSH_HEAD_REPLACE); - } - - public void testCreateSchema_ApprovalCategory_Owner() throws OrmException { - final ReviewDb c = db.create().open(); - try { - final ApprovalCategory cat; - - cat = c.approvalCategories().get(ApprovalCategory.OWN); - assertNotNull(cat); - assertEquals(ApprovalCategory.OWN, cat.getId()); - assertEquals("Owner", cat.getName()); - assertNull(cat.getAbbreviatedName()); - assertEquals(NoOpFunction.NAME, cat.getFunctionName()); - assertTrue(cat.isAction()); - } finally { - c.close(); - } - assertValueRange(ApprovalCategory.OWN, 1); - } - private void assertValueRange(ApprovalCategory.Id cat, int... range) throws OrmException { final HashSet<ApprovalCategoryValue.Id> act = @@ -314,57 +196,4 @@ fail("Category " + cat + " has additional values: " + act); } } - - public void testCreateSchema_DefaultAccess_AnonymousUsers() - throws OrmException { - db.create(); - final SystemConfig config = db.getSystemConfig(); - assertDefaultRight("refs/*", config.anonymousGroupId, - ApprovalCategory.READ, 1, 1); - } - - public void testCreateSchema_DefaultAccess_RegisteredUsers() - throws OrmException { - db.create(); - final SystemConfig config = db.getSystemConfig(); - assertDefaultRight("refs/*", config.registeredGroupId, - ApprovalCategory.READ, 1, 2); - assertDefaultRight("refs/heads/*", config.registeredGroupId, codeReview, - -1, 1); - } - - public void testCreateSchema_DefaultAccess_Administrators() - throws OrmException { - db.create(); - final SystemConfig config = db.getSystemConfig(); - assertDefaultRight("refs/*", config.adminGroupId, ApprovalCategory.READ, 1, - 1); - } - - private void assertDefaultRight(final String pattern, - final AccountGroup.Id group, final ApprovalCategory.Id category, int min, - int max) throws OrmException { - final ReviewDb c = db.open(); - try { - final SystemConfig cfg; - final Project all; - final RefRight right; - - cfg = c.systemConfig().get(new SystemConfig.Key()); - all = c.projects().get(cfg.wildProjectName); - right = - c.refRights().get( - new RefRight.Key(all.getNameKey(), new RefRight.RefPattern( - pattern), category, group)); - - assertNotNull(right); - assertEquals(all.getNameKey(), right.getProjectNameKey()); - assertEquals(group, right.getAccountGroupId()); - assertEquals(category, right.getApprovalCategoryId()); - assertEquals(min, right.getMinValue()); - assertEquals(max, right.getMaxValue()); - } finally { - c.close(); - } - } }
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java index a009f24..f2ba70e 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java +++ b/gerrit-server/src/test/java/com/google/gerrit/server/schema/SchemaUpdaterTest.java
@@ -16,7 +16,12 @@ import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.reviewdb.SystemConfig; +import com.google.gerrit.server.GerritPersonIdent; +import com.google.gerrit.server.GerritPersonIdentProvider; +import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.SitePaths; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.git.LocalDiskRepositoryManager; import com.google.gerrit.testutil.InMemoryDatabase; import com.google.gwtorm.client.OrmException; import com.google.gwtorm.client.SchemaFactory; @@ -27,6 +32,9 @@ import junit.framework.TestCase; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.PersonIdent; + import java.io.File; import java.io.FileNotFoundException; import java.util.List; @@ -58,6 +66,22 @@ bind(new TypeLiteral<SchemaFactory<ReviewDb>>() {}).toInstance(db); bind(SitePaths.class).toInstance(paths); install(new SchemaVersion.Module()); + + Config cfg = new Config(); + cfg.setString("gerrit", null, "basePath", "git"); + cfg.setString("user", null, "name", "Gerrit Code Review"); + cfg.setString("user", null, "email", "gerrit@localhost"); + + bind(Config.class) // + .annotatedWith(GerritServerConfig.class) // + .toInstance(cfg); + + bind(PersonIdent.class) // + .annotatedWith(GerritPersonIdent.class) // + .toProvider(GerritPersonIdentProvider.class); + + bind(GitRepositoryManager.class) // + .to(LocalDiskRepositoryManager.class); } }).getInstance(SchemaUpdater.class);
diff --git a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java index fe138c6..d95f17a4 100644 --- a/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java +++ b/gerrit-server/src/test/java/com/google/gerrit/testutil/InMemoryDatabase.java
@@ -17,7 +17,13 @@ import com.google.gerrit.reviewdb.CurrentSchemaVersion; import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.reviewdb.SystemConfig; +import com.google.gerrit.server.GerritPersonIdent; +import com.google.gerrit.server.GerritPersonIdentProvider; +import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.config.SitePath; import com.google.gerrit.server.config.SystemConfigProvider; +import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.git.LocalDiskRepositoryManager; import com.google.gerrit.server.schema.Current; import com.google.gerrit.server.schema.SchemaCreator; import com.google.gerrit.server.schema.SchemaVersion; @@ -25,13 +31,19 @@ import com.google.gwtorm.client.SchemaFactory; import com.google.gwtorm.jdbc.Database; import com.google.gwtorm.jdbc.SimpleDataSource; +import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Key; import com.google.inject.Provider; import junit.framework.TestCase; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.lib.Config; +import org.eclipse.jgit.lib.PersonIdent; + import java.io.File; +import java.io.IOException; import java.sql.Connection; import java.sql.SQLException; import java.util.Properties; @@ -84,8 +96,33 @@ database = new Database<ReviewDb>(dataSource, ReviewDb.class); schemaVersion = - Guice.createInjector(new SchemaVersion.Module()).getBinding( - Key.get(SchemaVersion.class, Current.class)).getProvider().get(); + Guice.createInjector(new AbstractModule() { + @Override + protected void configure() { + install(new SchemaVersion.Module()); + + bind(File.class) // + .annotatedWith(SitePath.class) // + .toInstance(new File(".")); + + Config cfg = new Config(); + cfg.setString("gerrit", null, "basePath", "git"); + cfg.setString("user", null, "name", "Gerrit Code Review"); + cfg.setString("user", null, "email", "gerrit@localhost"); + + bind(Config.class) // + .annotatedWith(GerritServerConfig.class) // + .toInstance(cfg); + + bind(PersonIdent.class) // + .annotatedWith(GerritPersonIdent.class) // + .toProvider(GerritPersonIdentProvider.class); + + bind(GitRepositoryManager.class) // + .to(LocalDiskRepositoryManager.class); + } + }).getBinding(Key.get(SchemaVersion.class, Current.class)) + .getProvider().get(); } catch (SQLException e) { throw new OrmException(e); } @@ -106,7 +143,14 @@ created = true; final ReviewDb c = open(); try { - new SchemaCreator(new File("."), schemaVersion).create(c); + try { + new SchemaCreator(new File("."), schemaVersion, null, + new PersonIdent("name", "email@site")).create(c); + } catch (IOException e) { + throw new OrmException("Cannot create in-memory database", e); + } catch (ConfigInvalidException e) { + throw new OrmException("Cannot create in-memory database", e); + } } finally { c.close(); }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java index 5b8edf0..61e8bfb 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/SshModule.java
@@ -32,6 +32,7 @@ import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.server.ssh.SshInfo; import com.google.gerrit.sshd.args4j.AccountGroupIdHandler; +import com.google.gerrit.sshd.args4j.AccountGroupUUIDHandler; import com.google.gerrit.sshd.args4j.AccountIdHandler; import com.google.gerrit.sshd.args4j.PatchSetIdHandler; import com.google.gerrit.sshd.args4j.ProjectControlHandler; @@ -117,6 +118,7 @@ registerOptionHandler(Account.Id.class, AccountIdHandler.class); registerOptionHandler(AccountGroup.Id.class, AccountGroupIdHandler.class); + registerOptionHandler(AccountGroup.UUID.class, AccountGroupUUIDHandler.class); registerOptionHandler(PatchSet.Id.class, PatchSetIdHandler.class); registerOptionHandler(ProjectControl.class, ProjectControlHandler.class); registerOptionHandler(SocketAddress.class, SocketAddressHandler.class);
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountGroupUUIDHandler.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountGroupUUIDHandler.java new file mode 100644 index 0000000..90b4987 --- /dev/null +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/args4j/AccountGroupUUIDHandler.java
@@ -0,0 +1,57 @@ +// Copyright (C) 2009 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.sshd.args4j; + +import com.google.gerrit.reviewdb.AccountGroup; +import com.google.gerrit.server.account.GroupCache; +import com.google.inject.Inject; +import com.google.inject.assistedinject.Assisted; + +import org.kohsuke.args4j.CmdLineException; +import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.OptionDef; +import org.kohsuke.args4j.spi.OptionHandler; +import org.kohsuke.args4j.spi.Parameters; +import org.kohsuke.args4j.spi.Setter; + +public class AccountGroupUUIDHandler extends OptionHandler<AccountGroup.UUID> { + private final GroupCache groupCache; + + @SuppressWarnings({"unchecked", "rawtypes"}) + @Inject + public AccountGroupUUIDHandler(final GroupCache groupCache, + @Assisted final CmdLineParser parser, @Assisted final OptionDef option, + @Assisted final Setter setter) { + super(parser, option, setter); + this.groupCache = groupCache; + } + + @Override + public final int parseArguments(final Parameters params) + throws CmdLineException { + final String n = params.getParameter(0); + final AccountGroup group = groupCache.get(new AccountGroup.NameKey(n)); + if (group == null) { + throw new CmdLineException(owner, "Group \"" + n + "\" does not exist"); + } + setter.addValue(group.getGroupUUID()); + return 1; + } + + @Override + public final String getDefaultMetaVariable() { + return "GROUP"; + } +}
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java index 07c573d..feb4891 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/AdminSetParent.java
@@ -15,22 +15,24 @@ package com.google.gerrit.sshd.commands; import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.server.config.WildProjectName; +import com.google.gerrit.server.git.MetaDataUpdate; +import com.google.gerrit.server.git.ProjectConfig; import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.sshd.AdminCommand; import com.google.gerrit.sshd.BaseCommand; -import com.google.gwtorm.client.OrmException; import com.google.inject.Inject; import org.apache.sshd.server.Environment; +import org.eclipse.jgit.errors.ConfigInvalidException; +import org.eclipse.jgit.errors.RepositoryNotFoundException; import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.Option; +import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -44,10 +46,10 @@ private List<ProjectControl> children = new ArrayList<ProjectControl>(); @Inject - private ReviewDb db; + private ProjectCache projectCache; @Inject - private ProjectCache projectCache; + private MetaDataUpdate.User metaDataUpdateFactory; @Inject @WildProjectName @@ -64,7 +66,7 @@ }); } - private void updateParents() throws OrmException, UnloggedFailure { + private void updateParents() throws Failure { final StringBuilder err = new StringBuilder(); final Set<Project.NameKey> grandParents = new HashSet<Project.NameKey>(); Project.NameKey newParentKey; @@ -112,22 +114,27 @@ continue; } - final Project child = db.projects().get(key); - if (child == null) { - // Race condition? Its in the cache, but not the database. - // - err.append("error: Project '" + name + "' not found\n"); - continue; + try { + MetaDataUpdate md = metaDataUpdateFactory.create(key); + try { + ProjectConfig config = ProjectConfig.read(md); + config.getProject().setParentName(newParentKey.get()); + md.setMessage("Inherit access from " + newParentKey.get() + "\n"); + if (!config.commit(md)) { + err.append("error: Could not update project " + name + "\n"); + } + } finally { + md.close(); + } + } catch (RepositoryNotFoundException notFound) { + err.append("error: Project " + name + " not found\n"); + } catch (IOException e) { + throw new Failure(1, "Cannot update project " + name, e); + } catch (ConfigInvalidException e) { + throw new Failure(1, "Cannot update project " + name, e); } - - child.setParent(newParentKey); - db.projects().update(Collections.singleton(child)); } - // Invalidate all projects in cache since inherited rights were changed. - // - projectCache.evictAll(); - if (err.length() > 0) { while (err.charAt(err.length() - 1) == '\n') { err.setLength(err.length() - 1);
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProject.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProject.java index 0be02fd..77dc8f1 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProject.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/CreateProject.java
@@ -15,26 +15,30 @@ package com.google.gerrit.sshd.commands; import com.google.gerrit.common.CollectionsUtil; +import com.google.gerrit.common.data.AccessSection; +import com.google.gerrit.common.data.GroupReference; +import com.google.gerrit.common.data.Permission; +import com.google.gerrit.common.data.PermissionRule; import com.google.gerrit.reviewdb.AccountGroup; -import com.google.gerrit.reviewdb.ApprovalCategory; import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.RefRight; -import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.reviewdb.Project.SubmitType; import com.google.gerrit.server.GerritPersonIdent; import com.google.gerrit.server.IdentifiedUser; +import com.google.gerrit.server.account.GroupCache; import com.google.gerrit.server.config.ProjectCreatorGroups; import com.google.gerrit.server.config.ProjectOwnerGroups; import com.google.gerrit.server.git.GitRepositoryManager; +import com.google.gerrit.server.git.MetaDataUpdate; +import com.google.gerrit.server.git.ProjectConfig; import com.google.gerrit.server.git.ReplicationQueue; +import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.sshd.BaseCommand; -import com.google.gwtorm.client.OrmException; import com.google.inject.Inject; import org.apache.sshd.server.Environment; +import org.eclipse.jgit.errors.ConfigInvalidException; import org.eclipse.jgit.lib.CommitBuilder; -import org.eclipse.jgit.lib.ConfigConstants; import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectInserter; @@ -42,7 +46,6 @@ import org.eclipse.jgit.lib.RefUpdate; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.lib.RefUpdate.Result; -import org.eclipse.jgit.lib.StoredConfig; import org.kohsuke.args4j.Option; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +53,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; -import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -63,14 +65,11 @@ private String projectName; @Option(name = "--owner", aliases = {"-o"}, usage = "owner(s) of project") - private List<AccountGroup.Id> ownerIds; + private List<AccountGroup.UUID> ownerIds; @Option(name = "--parent", aliases = {"-p"}, metaVar = "NAME", usage = "parent project") private ProjectControl newParent; - @Option(name = "--permissions-only", usage = "create project for use only as parent") - private boolean permissionsOnly; - @Option(name = "--description", aliases = {"-d"}, metaVar = "DESC", usage = "description of project") private String projectDescription = ""; @@ -98,18 +97,21 @@ private boolean createEmptyCommit; @Inject - private ReviewDb db; - - @Inject private GitRepositoryManager repoManager; @Inject + private ProjectCache projectCache; + + @Inject + private GroupCache groupCache; + + @Inject @ProjectCreatorGroups - private Set<AccountGroup.Id> projectCreatorGroups; + private Set<AccountGroup.UUID> projectCreatorGroups; @Inject @ProjectOwnerGroups - private Set<AccountGroup.Id> projectOwnerGroups; + private Set<AccountGroup.UUID> projectOwnerGroups; @Inject private IdentifiedUser currentUser; @@ -121,6 +123,9 @@ @GerritPersonIdent private PersonIdent serverIdent; + @Inject + MetaDataUpdate.User metaDataUpdateFactory; + private Project.NameKey nameKey; @Override @@ -136,41 +141,28 @@ validateParameters(); nameKey = new Project.NameKey(projectName); - if (!permissionsOnly) { - final Repository repo = repoManager.createRepository(nameKey); - try { - repo.create(true); + final Repository repo = repoManager.createRepository(nameKey); + try { + RefUpdate u = repo.updateRef(Constants.HEAD); + u.disableRefLog(); + u.link(branch); - StoredConfig config = repo.getConfig(); - config.setBoolean(ConfigConstants.CONFIG_CORE_SECTION, - null, ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true); - config.save(); - - RefUpdate u = repo.updateRef(Constants.HEAD); - u.disableRefLog(); - u.link(branch); - - repoManager.setProjectDescription(nameKey, projectDescription); - - createProject(); - - rq.replicateNewProject(nameKey, branch); - - if (createEmptyCommit) { - createEmptyCommit(repo, nameKey, branch); - } - } finally { - repo.close(); - } - } else { createProject(); + repoManager.setProjectDescription(nameKey, projectDescription); + + if (createEmptyCommit) { + createEmptyCommit(repo, nameKey, branch); + } + + rq.replicateNewProject(nameKey, branch); + } finally { + repo.close(); } } catch (Exception e) { p.print("Error when trying to create project: " + e.getMessage() + "\n"); p.flush(); } - } }); } @@ -181,9 +173,9 @@ try { CommitBuilder cb = new CommitBuilder(); cb.setTreeId(oi.insert(Constants.OBJ_TREE, new byte[] {})); + cb.setAuthor(metaDataUpdateFactory.getUserPersonIdent()); cb.setCommitter(serverIdent); - cb.setAuthor(cb.getCommitter()); - cb.setMessage("Initial empty repository"); + cb.setMessage("Initial empty repository\n"); ObjectId id = oi.insert(cb); oi.flush(); @@ -207,31 +199,41 @@ } } - private void createProject() throws OrmException { - List<RefRight> access = new ArrayList<RefRight>(); - for (AccountGroup.Id ownerId : ownerIds) { - final RefRight.Key prk = - new RefRight.Key(nameKey, new RefRight.RefPattern( - RefRight.ALL), ApprovalCategory.OWN, ownerId); - final RefRight pr = new RefRight(prk); - pr.setMaxValue((short) 1); - pr.setMinValue((short) 1); - access.add(pr); - } - db.refRights().insert(access); + private void createProject() throws IOException, ConfigInvalidException { + MetaDataUpdate md = metaDataUpdateFactory.create(nameKey); + try { + ProjectConfig config = ProjectConfig.read(md); + config.load(md); - final Project newProject = new Project(nameKey); - newProject.setDescription(projectDescription); - newProject.setSubmitType(submitType); - newProject.setUseContributorAgreements(contributorAgreements); - newProject.setUseSignedOffBy(signedOffBy); - newProject.setUseContentMerge(contentMerge); - newProject.setRequireChangeID(requireChangeID); - if (newParent != null) { - newProject.setParent(newParent.getProject().getNameKey()); - } + Project newProject = config.getProject(); + newProject.setDescription(projectDescription); + newProject.setSubmitType(submitType); + newProject.setUseContributorAgreements(contributorAgreements); + newProject.setUseSignedOffBy(signedOffBy); + newProject.setUseContentMerge(contentMerge); + newProject.setRequireChangeID(requireChangeID); + if (newParent != null) { + newProject.setParentName(newParent.getProject().getName()); + } - db.projects().insert(Collections.singleton(newProject)); + if (!ownerIds.isEmpty()) { + AccessSection all = config.getAccessSection(AccessSection.ALL, true); + for (AccountGroup.UUID ownerId : ownerIds) { + AccountGroup accountGroup = groupCache.get(ownerId); + GroupReference group = config.resolve(accountGroup); + all.getPermission(Permission.OWNER, true).add( + new PermissionRule(group)); + } + } + + md.setMessage("Created project\n"); + if (!config.commit(md)) { + throw new IOException("Cannot create " + projectName); + } + } finally { + md.close(); + } + projectCache.onCreateProject(nameKey); } private void validateParameters() throws Failure { @@ -246,9 +248,9 @@ if (ownerIds != null && !ownerIds.isEmpty()) { ownerIds = - new ArrayList<AccountGroup.Id>(new HashSet<AccountGroup.Id>(ownerIds)); + new ArrayList<AccountGroup.UUID>(new HashSet<AccountGroup.UUID>(ownerIds)); } else { - ownerIds = new ArrayList<AccountGroup.Id>(projectOwnerGroups); + ownerIds = new ArrayList<AccountGroup.UUID>(projectOwnerGroups); } while (branch.startsWith("/")) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjects.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjects.java index bb04f7a..b70b743 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjects.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjects.java
@@ -15,15 +15,12 @@ package com.google.gerrit.sshd.commands; import com.google.gerrit.reviewdb.Project; -import com.google.gerrit.reviewdb.ReviewDb; import com.google.gerrit.server.IdentifiedUser; -import com.google.gerrit.server.config.WildProjectName; import com.google.gerrit.server.git.GitRepositoryManager; import com.google.gerrit.server.project.ProjectCache; import com.google.gerrit.server.project.ProjectControl; import com.google.gerrit.server.project.ProjectState; import com.google.gerrit.sshd.BaseCommand; -import com.google.gwtorm.client.OrmException; import com.google.inject.Inject; import org.apache.sshd.server.Environment; @@ -44,9 +41,6 @@ private static final String NOT_VISIBLE_PROJECT = "(x)"; @Inject - private ReviewDb db; - - @Inject private IdentifiedUser currentUser; @Inject @@ -55,10 +49,6 @@ @Inject private GitRepositoryManager repoManager; - @Inject - @WildProjectName - private Project.NameKey wildProject; - @Option(name = "--show-branch", aliases = {"-b"}, usage = "displays the sha of each project in the specified branch") private String showBranch; @@ -93,14 +83,8 @@ } try { - for (final Project p : db.projects().all()) { - if (p.getNameKey().equals(wildProject)) { - // This project "doesn't exist". At least not as a repository. - // - continue; - } - - final ProjectState e = projectCache.get(p.getNameKey()); + for (final Project.NameKey projectName : projectCache.all()) { + final ProjectState e = projectCache.get(projectName); if (e == null) { // If we can't get it from the cache, pretend its not present. // @@ -118,7 +102,7 @@ } if (showBranch != null) { - final Ref ref = getBranchRef(p.getNameKey()); + final Ref ref = getBranchRef(projectName); if (ref == null || ref.getObjectId() == null || !pctl.controlForRef(ref.getLeaf().getName()).isVisible()) { // No branch, or the user can't see this branch, so skip it. @@ -130,9 +114,10 @@ stdout.print(' '); } - stdout.print(p.getName() + "\n"); + stdout.print(projectName.get() + "\n"); } else { - treeMap.put(p.getName(), new TreeNode(p, pctl.isVisible())); + treeMap.put(projectName.get(), + new TreeNode(pctl.getProject(), pctl.isVisible())); } } @@ -144,7 +129,7 @@ for (final TreeNode key : treeMap.values()) { final String parentName = key.getParentName(); if (parentName != null) { - final TreeNode node = treeMap.get((String)parentName); + final TreeNode node = treeMap.get(parentName); if (node != null) { node.addChild(key); } else { @@ -161,8 +146,6 @@ printElement(stdout, fakeRoot, -1, false, sortedNodes.get(sortedNodes.size() - 1)); stdout.flush(); } - } catch (OrmException e) { - throw new Failure(1, "fatal: database error", e); } finally { stdout.flush(); }
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java index 47bae4f..0a6033b 100644 --- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java +++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ReviewCommand.java
@@ -331,7 +331,7 @@ functionStateFactory.create(changeControl.getChange(), patchSetId, Collections.<PatchSetApproval> emptyList()); psa.setValue(v); - fs.normalize(approvalTypes.getApprovalType(psa.getCategoryId()), psa); + fs.normalize(approvalTypes.byId(psa.getCategoryId()), psa); if (v != psa.getValue()) { throw error(ao.name() + "=" + ao.value() + " not permitted"); }
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/SitePathFromSystemConfigProvider.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/SitePathFromSystemConfigProvider.java new file mode 100644 index 0000000..3bbcc98 --- /dev/null +++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/SitePathFromSystemConfigProvider.java
@@ -0,0 +1,61 @@ +// Copyright (C) 2009 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.httpd; + +import com.google.gerrit.reviewdb.ReviewDb; +import com.google.gerrit.reviewdb.SystemConfig; +import com.google.gerrit.server.config.SitePath; +import com.google.gwtorm.client.OrmException; +import com.google.gwtorm.client.SchemaFactory; +import com.google.inject.Inject; +import com.google.inject.Provider; + +import java.io.File; +import java.util.List; + +/** Provides {@link java.io.File} annotated with {@link SitePath}. */ +class SitePathFromSystemConfigProvider implements Provider<File> { + private final File path; + + @Inject + SitePathFromSystemConfigProvider(SchemaFactory<ReviewDb> schemaFactory) + throws OrmException { + path = read(schemaFactory); + } + + @Override + public File get() { + return path; + } + + private static File read(SchemaFactory<ReviewDb> schemaFactory) + throws OrmException { + ReviewDb db = schemaFactory.open(); + try { + List<SystemConfig> all = db.systemConfig().all().toList(); + switch (all.size()) { + case 1: + return new File(all.get(0).sitePath); + case 0: + throw new OrmException("system_config table is empty"); + default: + throw new OrmException("system_config must have exactly 1 row;" + + " found " + all.size() + " rows instead"); + } + } finally { + db.close(); + } + } +}
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java index 19c16ca..f1ac945 100644 --- a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java +++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
@@ -25,9 +25,9 @@ import com.google.gerrit.server.config.GerritServerConfigModule; import com.google.gerrit.server.config.MasterNodeStartup; import com.google.gerrit.server.config.SitePath; -import com.google.gerrit.server.config.SitePathFromSystemConfigProvider; import com.google.gerrit.server.schema.DataSourceProvider; import com.google.gerrit.server.schema.DatabaseModule; +import com.google.gerrit.server.schema.SchemaModule; import com.google.gerrit.sshd.SshModule; import com.google.gerrit.sshd.commands.MasterCommandModule; import com.google.inject.AbstractModule; @@ -166,6 +166,7 @@ }); modules.add(new GerritServerConfigModule()); } + modules.add(new SchemaModule()); modules.add(new AuthConfigModule()); return dbInjector.createChildInjector(modules); }