diff --git a/src/applications/differential/xaction/DifferentialRevisionActionTransaction.php b/src/applications/differential/xaction/DifferentialRevisionActionTransaction.php --- a/src/applications/differential/xaction/DifferentialRevisionActionTransaction.php +++ b/src/applications/differential/xaction/DifferentialRevisionActionTransaction.php @@ -74,9 +74,15 @@ DifferentialRevision $revision, PhabricatorUser $viewer) { + // Actions in the "review" group, like "Accept Revision", do not require + // that the actor be able to edit the revision. + $group_review = DifferentialRevisionEditEngine::ACTIONGROUP_REVIEW; + $is_review = ($this->getRevisionActionGroupKey() == $group_review); + $field = id(new PhabricatorApplyEditField()) ->setKey($this->getRevisionActionKey()) ->setTransactionType($this->getTransactionTypeConstant()) + ->setCanApplyWithoutEditCapability($is_review) ->setValue(true); if ($this->isActionAvailable($revision, $viewer)) { diff --git a/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php b/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php --- a/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php +++ b/src/applications/diffusion/xaction/DiffusionCommitActionTransaction.php @@ -70,9 +70,15 @@ PhabricatorRepositoryCommit $commit, PhabricatorUser $viewer) { + // Actions in the "audit" group, like "Accept Commit", do not require + // that the actor be able to edit the commit. + $group_audit = DiffusionCommitEditEngine::ACTIONGROUP_AUDIT; + $is_audit = ($this->getCommitActionGroupKey() == $group_audit); + $field = id(new PhabricatorApplyEditField()) ->setKey($this->getCommitActionKey()) ->setTransactionType($this->getTransactionTypeConstant()) + ->setCanApplyWithoutEditCapability($is_audit) ->setValue(true); if ($this->isActionAvailable($commit, $viewer)) { diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -1586,12 +1586,23 @@ $fields = $this->buildEditFields($object); + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $object, + PhabricatorPolicyCapability::CAN_EDIT); + $comment_actions = array(); foreach ($fields as $field) { if (!$field->shouldGenerateTransactionsFromComment()) { continue; } + if (!$can_edit) { + if (!$field->getCanApplyWithoutEditCapability()) { + continue; + } + } + $comment_action = $field->getCommentAction(); if (!$comment_action) { continue; @@ -1812,6 +1823,11 @@ $xactions = array(); + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $object, + PhabricatorPolicyCapability::CAN_EDIT); + if ($actions) { $action_map = array(); foreach ($actions as $action) { @@ -1834,6 +1850,21 @@ continue; } + // If you don't have edit permission on the object, you're limited in + // which actions you can take via the comment form. Most actions + // need edit permission, but some actions (like "Accept Revision") + // can be applied by anyone with view permission. + if (!$can_edit) { + if (!$field->getCanApplyWithoutEditCapability()) { + // We know the user doesn't have the capability, so this will + // raise a policy exception. + PhabricatorPolicyFilter::requireCapability( + $viewer, + $object, + PhabricatorPolicyCapability::CAN_EDIT); + } + } + if (array_key_exists('initialValue', $action)) { $field->setInitialValue($action['initialValue']); } diff --git a/src/applications/transactions/editfield/PhabricatorEditField.php b/src/applications/transactions/editfield/PhabricatorEditField.php --- a/src/applications/transactions/editfield/PhabricatorEditField.php +++ b/src/applications/transactions/editfield/PhabricatorEditField.php @@ -36,6 +36,7 @@ private $isEditDefaults; private $isSubmittedForm; private $controlError; + private $canApplyWithoutEditCapability = false; private $isReorderable = true; private $isDefaultable = true; @@ -292,6 +293,15 @@ return $this->controlInstructions; } + public function setCanApplyWithoutEditCapability($can_apply) { + $this->canApplyWithoutEditCapability = $can_apply; + return $this; + } + + public function getCanApplyWithoutEditCapability() { + return $this->canApplyWithoutEditCapability; + } + protected function newControl() { throw new PhutilMethodNotImplementedException(); } diff --git a/src/applications/transactions/engineextension/PhabricatorCommentEditEngineExtension.php b/src/applications/transactions/engineextension/PhabricatorCommentEditEngineExtension.php --- a/src/applications/transactions/engineextension/PhabricatorCommentEditEngineExtension.php +++ b/src/applications/transactions/engineextension/PhabricatorCommentEditEngineExtension.php @@ -39,6 +39,10 @@ $comment_type = PhabricatorTransactions::TYPE_COMMENT; + // Comments have a lot of special behavior which doesn't always check + // this flag, but we set it for consistency. + $is_interact = true; + $comment_field = id(new PhabricatorCommentEditField()) ->setKey(self::EDITKEY) ->setLabel(pht('Comments')) @@ -47,6 +51,7 @@ ->setIsReorderable(false) ->setIsDefaultable(false) ->setIsLockable(false) + ->setCanApplyWithoutEditCapability($is_interact) ->setTransactionType($comment_type) ->setConduitDescription(pht('Make comments.')) ->setConduitTypeDescription(