summaryrefslogtreecommitdiffstats
path: root/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
diff options
context:
space:
mode:
Diffstat (limited to 'gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java')
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java591
1 files changed, 0 insertions, 591 deletions
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
deleted file mode 100644
index cde2c8098e..0000000000
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/RefControl.java
+++ /dev/null
@@ -1,591 +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.project;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-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.extensions.restapi.AuthException;
-import com.google.gerrit.reviewdb.client.Change;
-import com.google.gerrit.reviewdb.client.Project;
-import com.google.gerrit.reviewdb.client.RefNames;
-import com.google.gerrit.server.CurrentUser;
-import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.permissions.FailedPermissionBackend;
-import com.google.gerrit.server.permissions.PermissionBackend.ForChange;
-import com.google.gerrit.server.permissions.PermissionBackend.ForRef;
-import com.google.gerrit.server.permissions.PermissionBackendException;
-import com.google.gerrit.server.permissions.RefPermission;
-import com.google.gerrit.server.query.change.ChangeData;
-import com.google.gwtorm.server.OrmException;
-import com.google.inject.util.Providers;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/** Manages access control for Git references (aka branches, tags). */
-public class RefControl {
- private final ProjectControl projectControl;
- private final String refName;
-
- /** All permissions that apply to this reference. */
- private final PermissionCollection relevant;
-
- /** Cached set of permissions matching this user. */
- private final Map<String, List<PermissionRule>> effective;
-
- private Boolean owner;
- private Boolean canForgeAuthor;
- private Boolean canForgeCommitter;
- private Boolean isVisible;
-
- RefControl(ProjectControl projectControl, String ref, PermissionCollection relevant) {
- this.projectControl = projectControl;
- this.refName = ref;
- this.relevant = relevant;
- this.effective = new HashMap<>();
- }
-
- public String getRefName() {
- return refName;
- }
-
- public ProjectControl getProjectControl() {
- return projectControl;
- }
-
- public CurrentUser getUser() {
- return projectControl.getUser();
- }
-
- public RefControl forUser(CurrentUser who) {
- ProjectControl newCtl = projectControl.forUser(who);
- if (relevant.isUserSpecific()) {
- return newCtl.controlForRef(getRefName());
- }
- return new RefControl(newCtl, getRefName(), relevant);
- }
-
- /** Is this user a ref owner? */
- public boolean isOwner() {
- if (owner == null) {
- if (canPerform(Permission.OWNER)) {
- owner = true;
-
- } else {
- owner = projectControl.isOwner();
- }
- }
- return owner;
- }
-
- /** Can this user see this reference exists? */
- boolean isVisible() {
- if (isVisible == null) {
- isVisible =
- (getUser().isInternalUser() || canPerform(Permission.READ))
- && isProjectStatePermittingRead();
- }
- return isVisible;
- }
-
- /** Can this user see other users change edits? */
- public boolean isEditVisible() {
- return canViewPrivateChanges();
- }
-
- private boolean canUpload() {
- return projectControl.controlForRef("refs/for/" + getRefName()).canPerform(Permission.PUSH)
- && isProjectStatePermittingWrite();
- }
-
- /** @return true if this user can add a new patch set to this ref */
- boolean canAddPatchSet() {
- return projectControl
- .controlForRef("refs/for/" + getRefName())
- .canPerform(Permission.ADD_PATCH_SET)
- && isProjectStatePermittingWrite();
- }
-
- /** @return true if this user can submit merge patch sets to this ref */
- private boolean canUploadMerges() {
- return projectControl
- .controlForRef("refs/for/" + getRefName())
- .canPerform(Permission.PUSH_MERGE)
- && isProjectStatePermittingWrite();
- }
-
- /** @return true if this user can rebase changes on this ref */
- boolean canRebase() {
- return canPerform(Permission.REBASE) && isProjectStatePermittingWrite();
- }
-
- /** @return true if this user can submit patch sets to this ref */
- boolean canSubmit(boolean isChangeOwner) {
- if (RefNames.REFS_CONFIG.equals(refName)) {
- // Always allow project owners to submit configuration changes.
- // Submitting configuration changes modifies the access control
- // rules. Allowing this to be done by a non-project-owner opens
- // a security hole enabling editing of access rules, and thus
- // granting of powers beyond submitting to the configuration.
- return projectControl.isOwner();
- }
- return canPerform(Permission.SUBMIT, isChangeOwner) && isProjectStatePermittingWrite();
- }
-
- /** @return true if the user can update the reference as a fast-forward. */
- private boolean canUpdate() {
- if (RefNames.REFS_CONFIG.equals(refName) && !projectControl.isOwner()) {
- // Pushing requires being at least project owner, in addition to push.
- // Pushing configuration changes modifies the access control
- // rules. Allowing this to be done by a non-project-owner opens
- // a security hole enabling editing of access rules, and thus
- // granting of powers beyond pushing to the configuration.
-
- // On the AllProjects project the owner access right cannot be assigned,
- // this why for the AllProjects project we allow administrators to push
- // configuration changes if they have push without being project owner.
- if (!(projectControl.getProjectState().isAllProjects() && projectControl.isAdmin())) {
- return false;
- }
- }
- return canPerform(Permission.PUSH) && isProjectStatePermittingWrite();
- }
-
- /** @return true if the user can rewind (force push) the reference. */
- private boolean canForceUpdate() {
- if (!isProjectStatePermittingWrite()) {
- return false;
- }
-
- if (canPushWithForce()) {
- return true;
- }
-
- switch (getUser().getAccessPath()) {
- case GIT:
- return false;
-
- case JSON_RPC:
- case REST_API:
- case SSH_COMMAND:
- case UNKNOWN:
- case WEB_BROWSER:
- default:
- return (isOwner() && !isForceBlocked(Permission.PUSH)) || projectControl.isAdmin();
- }
- }
-
- private boolean isProjectStatePermittingWrite() {
- return getProjectControl().getProject().getState().permitsWrite();
- }
-
- private boolean isProjectStatePermittingRead() {
- return getProjectControl().getProject().getState().permitsRead();
- }
-
- private boolean canPushWithForce() {
- if (!isProjectStatePermittingWrite()
- || (RefNames.REFS_CONFIG.equals(refName) && !projectControl.isOwner())) {
- // Pushing requires being at least project owner, in addition to push.
- // Pushing configuration changes modifies the access control
- // rules. Allowing this to be done by a non-project-owner opens
- // a security hole enabling editing of access rules, and thus
- // granting of powers beyond pushing to the configuration.
- return false;
- }
- return canForcePerform(Permission.PUSH);
- }
-
- /**
- * Determines whether the user can delete the Git ref controlled by this object.
- *
- * @return {@code true} if the user specified can delete a Git ref.
- */
- private boolean canDelete() {
- if (!isProjectStatePermittingWrite() || (RefNames.REFS_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 (getUser().getAccessPath()) {
- case GIT:
- return canPushWithForce() || canPerform(Permission.DELETE);
-
- case JSON_RPC:
- case REST_API:
- case SSH_COMMAND:
- case UNKNOWN:
- case WEB_BROWSER:
- default:
- return (isOwner() && !isForceBlocked(Permission.PUSH))
- || canPushWithForce()
- || canPerform(Permission.DELETE)
- || projectControl.isAdmin();
- }
- }
-
- /** @return true if this user can forge the author line in a commit. */
- private boolean canForgeAuthor() {
- if (canForgeAuthor == null) {
- canForgeAuthor = canPerform(Permission.FORGE_AUTHOR);
- }
- return canForgeAuthor;
- }
-
- /** @return true if this user can forge the committer line in a commit. */
- private boolean canForgeCommitter() {
- if (canForgeCommitter == null) {
- canForgeCommitter = canPerform(Permission.FORGE_COMMITTER);
- }
- return canForgeCommitter;
- }
-
- /** @return true if this user can forge the server on the committer line. */
- private boolean canForgeGerritServerIdentity() {
- return canPerform(Permission.FORGE_SERVER);
- }
-
- /** @return true if this user can abandon a change for this ref */
- boolean canAbandon() {
- return canPerform(Permission.ABANDON);
- }
-
- /** @return true if this user can remove a reviewer for a change. */
- boolean canRemoveReviewer() {
- return canPerform(Permission.REMOVE_REVIEWER);
- }
-
- /** @return true if this user can view private changes. */
- boolean canViewPrivateChanges() {
- return canPerform(Permission.VIEW_PRIVATE_CHANGES);
- }
-
- /** @return true if this user can delete changes. */
- boolean canDeleteChanges(boolean isChangeOwner) {
- return canPerform(Permission.DELETE_CHANGES)
- || (isChangeOwner && canPerform(Permission.DELETE_OWN_CHANGES, isChangeOwner));
- }
-
- /** @return true if this user can edit topic names. */
- boolean canEditTopicName() {
- return canPerform(Permission.EDIT_TOPIC_NAME);
- }
-
- /** @return true if this user can edit hashtag names. */
- boolean canEditHashtags() {
- return canPerform(Permission.EDIT_HASHTAGS);
- }
-
- boolean canEditAssignee() {
- return canPerform(Permission.EDIT_ASSIGNEE);
- }
-
- /** @return true if this user can force edit topic names. */
- boolean canForceEditTopicName() {
- return canForcePerform(Permission.EDIT_TOPIC_NAME);
- }
-
- /** The range of permitted values associated with a label permission. */
- PermissionRange getRange(String permission) {
- return getRange(permission, false);
- }
-
- /** The range of permitted values associated with a label permission. */
- PermissionRange getRange(String permission, boolean isChangeOwner) {
- if (Permission.hasRange(permission)) {
- return toRange(permission, access(permission, isChangeOwner));
- }
- return null;
- }
-
- private static class AllowedRange {
- private int allowMin;
- private int allowMax;
- private int blockMin = Integer.MIN_VALUE;
- private int blockMax = Integer.MAX_VALUE;
-
- void update(PermissionRule rule) {
- if (rule.isBlock()) {
- blockMin = Math.max(blockMin, rule.getMin());
- blockMax = Math.min(blockMax, rule.getMax());
- } else {
- allowMin = Math.min(allowMin, rule.getMin());
- allowMax = Math.max(allowMax, rule.getMax());
- }
- }
-
- int getAllowMin() {
- return allowMin;
- }
-
- int getAllowMax() {
- return allowMax;
- }
-
- int getBlockMin() {
- // ALLOW wins over BLOCK on the same project
- return Math.min(blockMin, allowMin - 1);
- }
-
- int getBlockMax() {
- // ALLOW wins over BLOCK on the same project
- return Math.max(blockMax, allowMax + 1);
- }
- }
-
- private PermissionRange toRange(String permissionName, List<PermissionRule> ruleList) {
- Map<ProjectRef, AllowedRange> ranges = new HashMap<>();
- for (PermissionRule rule : ruleList) {
- ProjectRef p = relevant.getRuleProps(rule);
- AllowedRange r = ranges.get(p);
- if (r == null) {
- r = new AllowedRange();
- ranges.put(p, r);
- }
- r.update(rule);
- }
- int allowMin = 0;
- int allowMax = 0;
- int blockMin = Integer.MIN_VALUE;
- int blockMax = Integer.MAX_VALUE;
- for (AllowedRange r : ranges.values()) {
- allowMin = Math.min(allowMin, r.getAllowMin());
- allowMax = Math.max(allowMax, r.getAllowMax());
- blockMin = Math.max(blockMin, r.getBlockMin());
- blockMax = Math.min(blockMax, r.getBlockMax());
- }
-
- // BLOCK wins over ALLOW across projects
- int min = Math.max(allowMin, blockMin + 1);
- int max = Math.min(allowMax, blockMax - 1);
- return new PermissionRange(permissionName, min, max);
- }
-
- /** True if the user has this permission. Works only for non labels. */
- boolean canPerform(String permissionName) {
- return canPerform(permissionName, false);
- }
-
- boolean canPerform(String permissionName, boolean isChangeOwner) {
- return doCanPerform(permissionName, isChangeOwner, false);
- }
-
- /** True if the user is blocked from using this permission. */
- public boolean isBlocked(String permissionName) {
- return !doCanPerform(permissionName, false, true);
- }
-
- private boolean doCanPerform(String permissionName, boolean isChangeOwner, boolean blockOnly) {
- List<PermissionRule> access = access(permissionName, isChangeOwner);
- List<PermissionRule> overridden = relevant.getOverridden(permissionName);
- Set<ProjectRef> allows = new HashSet<>();
- Set<ProjectRef> blocks = new HashSet<>();
- for (PermissionRule rule : access) {
- if (rule.isBlock() && !rule.getForce()) {
- blocks.add(relevant.getRuleProps(rule));
- } else {
- allows.add(relevant.getRuleProps(rule));
- }
- }
- for (PermissionRule rule : overridden) {
- blocks.remove(relevant.getRuleProps(rule));
- }
- blocks.removeAll(allows);
- return blocks.isEmpty() && (!allows.isEmpty() || blockOnly);
- }
-
- /** True if the user has force this permission. Works only for non labels. */
- private boolean canForcePerform(String permissionName) {
- List<PermissionRule> access = access(permissionName);
- List<PermissionRule> overridden = relevant.getOverridden(permissionName);
- Set<ProjectRef> allows = new HashSet<>();
- Set<ProjectRef> blocks = new HashSet<>();
- for (PermissionRule rule : access) {
- if (rule.isBlock()) {
- blocks.add(relevant.getRuleProps(rule));
- } else if (rule.getForce()) {
- allows.add(relevant.getRuleProps(rule));
- }
- }
- for (PermissionRule rule : overridden) {
- if (rule.getForce()) {
- blocks.remove(relevant.getRuleProps(rule));
- }
- }
- blocks.removeAll(allows);
- return blocks.isEmpty() && !allows.isEmpty();
- }
-
- /** True if for this permission force is blocked for the user. Works only for non labels. */
- private boolean isForceBlocked(String permissionName) {
- List<PermissionRule> access = access(permissionName);
- List<PermissionRule> overridden = relevant.getOverridden(permissionName);
- Set<ProjectRef> allows = new HashSet<>();
- Set<ProjectRef> blocks = new HashSet<>();
- for (PermissionRule rule : access) {
- if (rule.isBlock()) {
- blocks.add(relevant.getRuleProps(rule));
- } else if (rule.getForce()) {
- allows.add(relevant.getRuleProps(rule));
- }
- }
- for (PermissionRule rule : overridden) {
- if (rule.getForce()) {
- blocks.remove(relevant.getRuleProps(rule));
- }
- }
- blocks.removeAll(allows);
- return !blocks.isEmpty();
- }
-
- /** Rules for the given permission, or the empty list. */
- private List<PermissionRule> access(String permissionName) {
- return access(permissionName, false);
- }
-
- /** Rules for the given permission, or the empty list. */
- private List<PermissionRule> access(String permissionName, boolean isChangeOwner) {
- List<PermissionRule> rules = effective.get(permissionName);
- if (rules != null) {
- return rules;
- }
-
- rules = relevant.getPermission(permissionName);
-
- List<PermissionRule> mine = new ArrayList<>(rules.size());
- for (PermissionRule rule : rules) {
- if (projectControl.match(rule, isChangeOwner)) {
- mine.add(rule);
- }
- }
-
- if (mine.isEmpty()) {
- mine = Collections.emptyList();
- }
- effective.put(permissionName, mine);
- return mine;
- }
-
- ForRef asForRef() {
- return new ForRefImpl();
- }
-
- private class ForRefImpl extends ForRef {
- @Override
- public ForRef user(CurrentUser user) {
- return forUser(user).asForRef().database(db);
- }
-
- @Override
- public ForChange change(ChangeData cd) {
- try {
- // TODO(hiesel) Force callers to call database() and use db instead of cd.db()
- return getProjectControl()
- .controlFor(cd.db(), cd.change())
- .asForChange(cd, Providers.of(cd.db()));
- } catch (OrmException e) {
- return FailedPermissionBackend.change("unavailable", e);
- }
- }
-
- @Override
- public ForChange change(ChangeNotes notes) {
- Project.NameKey project = getProjectControl().getProject().getNameKey();
- Change change = notes.getChange();
- checkArgument(
- project.equals(change.getProject()),
- "expected change in project %s, not %s",
- project,
- change.getProject());
- return getProjectControl().controlFor(notes).asForChange(null, db);
- }
-
- @Override
- public ForChange indexedChange(ChangeData cd, ChangeNotes notes) {
- return getProjectControl().controlFor(notes).asForChange(cd, db);
- }
-
- @Override
- public void check(RefPermission perm) throws AuthException, PermissionBackendException {
- if (!can(perm)) {
- throw new AuthException(perm.describeForException() + " not permitted for " + getRefName());
- }
- }
-
- @Override
- public Set<RefPermission> test(Collection<RefPermission> permSet)
- throws PermissionBackendException {
- EnumSet<RefPermission> ok = EnumSet.noneOf(RefPermission.class);
- for (RefPermission perm : permSet) {
- if (can(perm)) {
- ok.add(perm);
- }
- }
- return ok;
- }
-
- private boolean can(RefPermission perm) throws PermissionBackendException {
- switch (perm) {
- case READ:
- return isVisible();
- case CREATE:
- // TODO This isn't an accurate test.
- return canPerform(perm.permissionName().get());
- case DELETE:
- return canDelete();
- case UPDATE:
- return canUpdate();
- case FORCE_UPDATE:
- return canForceUpdate();
-
- case FORGE_AUTHOR:
- return canForgeAuthor();
- case FORGE_COMMITTER:
- return canForgeCommitter();
- case FORGE_SERVER:
- return canForgeGerritServerIdentity();
- case MERGE:
- return canUploadMerges();
-
- case CREATE_CHANGE:
- return canUpload();
-
- case UPDATE_BY_SUBMIT:
- return projectControl.controlForRef("refs/for/" + getRefName()).canSubmit(true);
-
- case SKIP_VALIDATION:
- return canForgeAuthor()
- && canForgeCommitter()
- && canForgeGerritServerIdentity()
- && canUploadMerges()
- && !projectControl.getProjectState().isUseSignedOffBy();
- }
- throw new PermissionBackendException(perm + " unsupported");
- }
- }
-}