@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Make the "you can't edit away your edit capability" policy check generic

Summary:
Ref T4379. Currently, you can edit away your edit capability in Projects. Prevent this in a general way.

Since some objects have complex edit policies (like "the owner can always edit"), we can't just check the value itself. We also can't fairly assume that every object has a `setEditPolicy()` method, even though almost all do right now. Instead, provide a way to pretend we've completed the edit and changed the policy.

Test Plan: Unit tests, tried to edit away my edit capability.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T4379

Differential Revision: https://secure.phabricator.com/D8179

+84 -7
+61 -7
src/applications/policy/filter/PhabricatorPolicyFilter.php
··· 8 8 private $raisePolicyExceptions; 9 9 private $userProjects; 10 10 private $customPolicies = array(); 11 + private $forcedPolicy; 11 12 12 13 public static function mustRetainCapability( 13 14 PhabricatorUser $user, ··· 25 26 PhabricatorUser $user, 26 27 PhabricatorPolicyInterface $object, 27 28 $capability) { 28 - $filter = new PhabricatorPolicyFilter(); 29 - $filter->setViewer($user); 30 - $filter->requireCapabilities(array($capability)); 31 - $filter->raisePolicyExceptions(true); 32 - $filter->apply(array($object)); 29 + $filter = id(new PhabricatorPolicyFilter()) 30 + ->setViewer($user) 31 + ->requireCapabilities(array($capability)) 32 + ->raisePolicyExceptions(true) 33 + ->apply(array($object)); 34 + } 35 + 36 + /** 37 + * Perform a capability check, acting as though an object had a specific 38 + * policy. This is primarily used to check if a policy is valid (for example, 39 + * to prevent users from editing away their ability to edit an object). 40 + * 41 + * Specifically, a check like this: 42 + * 43 + * PhabricatorPolicyFilter::requireCapabilityWithForcedPolicy( 44 + * $viewer, 45 + * $object, 46 + * PhabricatorPolicyCapability::CAN_EDIT, 47 + * $potential_new_policy); 48 + * 49 + * ...will throw a @{class:PhabricatorPolicyException} if the new policy would 50 + * remove the user's ability to edit the object. 51 + * 52 + * @param PhabricatorUser The viewer to perform a policy check for. 53 + * @param PhabricatorPolicyInterface The object to perform a policy check on. 54 + * @param string Capability to test. 55 + * @param string Perform the test as though the object has this 56 + * policy instead of the policy it actually has. 57 + * @return void 58 + */ 59 + public static function requireCapabilityWithForcedPolicy( 60 + PhabricatorUser $viewer, 61 + PhabricatorPolicyInterface $object, 62 + $capability, 63 + $forced_policy) { 64 + 65 + id(new PhabricatorPolicyFilter()) 66 + ->setViewer($viewer) 67 + ->requireCapabilities(array($capability)) 68 + ->raisePolicyExceptions(true) 69 + ->forcePolicy($forced_policy) 70 + ->apply(array($object)); 33 71 } 34 72 35 73 public static function hasCapability( ··· 60 98 return $this; 61 99 } 62 100 101 + public function forcePolicy($forced_policy) { 102 + $this->forcedPolicy = $forced_policy; 103 + return $this; 104 + } 105 + 63 106 public function apply(array $objects) { 64 107 assert_instances_of($objects, 'PhabricatorPolicyInterface'); 65 108 ··· 96 139 "not have that capability!"); 97 140 } 98 141 99 - $policy = $object->getPolicy($capability); 142 + $policy = $this->getObjectPolicy($object, $capability); 100 143 $type = phid_get_type($policy); 101 144 if ($type == PhabricatorProjectPHIDTypeProject::TYPECONST) { 102 145 $need_projects[$policy] = $policy; ··· 169 212 PhabricatorPolicyInterface $object, 170 213 $capability) { 171 214 172 - $policy = $object->getPolicy($capability); 215 + $policy = $this->getObjectPolicy($object, $capability); 173 216 174 217 if (!$policy) { 175 218 // TODO: Formalize this somehow? ··· 400 443 } 401 444 402 445 return false; 446 + } 447 + 448 + private function getObjectPolicy( 449 + PhabricatorPolicyInterface $object, 450 + $capability) { 451 + 452 + if ($this->forcedPolicy) { 453 + return $this->forcedPolicy; 454 + } else { 455 + return $object->getPolicy($capability); 456 + } 403 457 } 404 458 405 459 }
+23
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 1144 1144 1145 1145 $errors = array(); 1146 1146 switch ($type) { 1147 + case PhabricatorTransactions::TYPE_EDIT_POLICY: 1148 + // Make sure the user isn't editing away their ability to edit this 1149 + // object. 1150 + foreach ($xactions as $xaction) { 1151 + try { 1152 + PhabricatorPolicyFilter::requireCapabilityWithForcedPolicy( 1153 + $this->requireActor(), 1154 + $object, 1155 + PhabricatorPolicyCapability::CAN_EDIT, 1156 + $xaction->getNewValue()); 1157 + } catch (PhabricatorPolicyException $ex) { 1158 + $errors[] = array( 1159 + new PhabricatorApplicationTransactionValidationError( 1160 + $type, 1161 + pht('Invalid'), 1162 + pht( 1163 + 'You can not select this edit policy, because you would '. 1164 + 'no longer be able to edit the object.'), 1165 + $xaction), 1166 + ); 1167 + } 1168 + } 1169 + break; 1147 1170 case PhabricatorTransactions::TYPE_CUSTOMFIELD: 1148 1171 $groups = array(); 1149 1172 foreach ($xactions as $xaction) {