@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.

Improve PolicyFilter and PolicyQuery

Summary:
- Allow PolicyQuery to require specific sets of capabilities other than "CAN_VIEW", like edit, etc. The default set is "view".
- Add some convenience methods to PolicyFilter to test for capabilities.

Test Plan: Viewed pastes, projects, etc. Used other stuff in future diff.

Reviewers: vrana, btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T603

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

+135 -52
+117 -51
src/applications/policy/filter/PhabricatorPolicyFilter.php
··· 20 20 21 21 private $viewer; 22 22 private $objects; 23 - private $capability; 23 + private $capabilities; 24 24 private $raisePolicyExceptions; 25 25 26 + public static function mustRetainCapability( 27 + PhabricatorUser $user, 28 + PhabricatorPolicyInterface $object, 29 + $capability) { 30 + 31 + if (!self::hasCapability($user, $object, $capability)) { 32 + throw new Exception( 33 + "You can not make that edit, because it would remove your ability ". 34 + "to '{$capability}' the object."); 35 + } 36 + } 37 + 38 + public static function requireCapability( 39 + PhabricatorUser $user, 40 + PhabricatorPolicyInterface $object, 41 + $capability) { 42 + $filter = new PhabricatorPolicyFilter(); 43 + $filter->setViewer($user); 44 + $filter->requireCapabilities(array($capability)); 45 + $filter->raisePolicyExceptions(true); 46 + $filter->apply(array($object)); 47 + } 48 + 49 + public static function hasCapability( 50 + PhabricatorUser $user, 51 + PhabricatorPolicyInterface $object, 52 + $capability) { 53 + 54 + $filter = new PhabricatorPolicyFilter(); 55 + $filter->setViewer($user); 56 + $filter->requireCapabilities(array($capability)); 57 + $result = $filter->apply(array($object)); 58 + 59 + return (count($result) == 1); 60 + } 61 + 26 62 public function setViewer(PhabricatorUser $user) { 27 63 $this->viewer = $user; 28 64 return $this; 29 65 } 30 66 31 - public function setCapability($capability) { 32 - $this->capability = $capability; 67 + public function requireCapabilities(array $capabilities) { 68 + $this->capabilities = $capabilities; 33 69 return $this; 34 70 } 35 71 ··· 41 77 public function apply(array $objects) { 42 78 assert_instances_of($objects, 'PhabricatorPolicyInterface'); 43 79 44 - $viewer = $this->viewer; 45 - $capability = $this->capability; 80 + $viewer = $this->viewer; 81 + $capabilities = $this->capabilities; 46 82 47 - if (!$viewer || !$capability) { 83 + if (!$viewer || !$capabilities) { 48 84 throw new Exception( 49 - 'Call setViewer() and setCapability() before apply()!'); 85 + 'Call setViewer() and requireCapabilities() before apply()!'); 50 86 } 51 87 52 88 $filtered = array(); 53 89 54 90 foreach ($objects as $key => $object) { 55 91 $object_capabilities = $object->getCapabilities(); 92 + foreach ($capabilities as $capability) { 93 + if (!in_array($capability, $object_capabilities)) { 94 + throw new Exception( 95 + "Testing for capability '{$capability}' on an object which does ". 96 + "not have that capability!"); 97 + } 56 98 57 - if (!in_array($capability, $object_capabilities)) { 58 - throw new Exception( 59 - "Testing for capability '{$capability}' on an object which does not ". 60 - "have that capability!"); 99 + if (!$this->checkCapability($object, $capability)) { 100 + // If we're missing any capability, move on to the next object. 101 + continue 2; 102 + } 61 103 } 62 104 63 - if ($object->hasAutomaticCapability($capability, $this->viewer)) { 64 - $filtered[$key] = $object; 65 - continue; 66 - } 105 + // If we make it here, we have all of the required capabilities. 106 + $filtered[$key] = $object; 107 + } 108 + 109 + return $filtered; 110 + } 111 + 112 + private function checkCapability( 113 + PhabricatorPolicyInterface $object, 114 + $capability) { 67 115 68 - $policy = $object->getPolicy($capability); 116 + $policy = $object->getPolicy($capability); 69 117 118 + if (!$policy) { 119 + // TODO: Formalize this somehow? 120 + $policy = PhabricatorPolicies::POLICY_USER; 121 + } 122 + 123 + if ($policy == PhabricatorPolicies::POLICY_PUBLIC) { 70 124 // If the object is set to "public" but that policy is disabled for this 71 125 // install, restrict the policy to "user". 72 - if ($policy == PhabricatorPolicies::POLICY_PUBLIC) { 73 - if (!PhabricatorEnv::getEnvConfig('policy.allow-public')) { 74 - $policy = PhabricatorPolicies::POLICY_USER; 75 - } 126 + if (!PhabricatorEnv::getEnvConfig('policy.allow-public')) { 127 + $policy = PhabricatorPolicies::POLICY_USER; 76 128 } 77 129 78 - switch ($policy) { 79 - case PhabricatorPolicies::POLICY_PUBLIC: 80 - $filtered[$key] = $object; 81 - break; 82 - case PhabricatorPolicies::POLICY_USER: 83 - if ($viewer->getPHID()) { 84 - $filtered[$key] = $object; 85 - } else { 86 - $this->rejectObject($object, $policy); 87 - } 88 - break; 89 - case PhabricatorPolicies::POLICY_ADMIN: 90 - if ($viewer->getIsAdmin()) { 91 - $filtered[$key] = $object; 92 - } else { 93 - $this->rejectObject($object, $policy); 94 - } 95 - break; 96 - case PhabricatorPolicies::POLICY_NOONE: 97 - $this->rejectObject($object, $policy); 98 - break; 99 - default: 100 - throw new Exception("Object has unknown policy '{$policy}'!"); 130 + // If the object is set to "public" but the capability is anything other 131 + // than "view", restrict the policy to "user". 132 + if ($capability != PhabricatorPolicyCapability::CAN_VIEW) { 133 + $policy = PhabricatorPolicies::POLICY_USER; 101 134 } 102 135 } 103 136 104 - return $filtered; 137 + $viewer = $this->viewer; 138 + 139 + if ($object->hasAutomaticCapability($capability, $viewer)) { 140 + return true; 141 + } 142 + 143 + switch ($policy) { 144 + case PhabricatorPolicies::POLICY_PUBLIC: 145 + return true; 146 + case PhabricatorPolicies::POLICY_USER: 147 + if ($viewer->getPHID()) { 148 + return true; 149 + } else { 150 + $this->rejectObject($object, $policy, $capability); 151 + } 152 + break; 153 + case PhabricatorPolicies::POLICY_ADMIN: 154 + if ($viewer->getIsAdmin()) { 155 + return true; 156 + } else { 157 + $this->rejectObject($object, $policy, $capability); 158 + } 159 + break; 160 + case PhabricatorPolicies::POLICY_NOONE: 161 + $this->rejectObject($object, $policy, $capability); 162 + break; 163 + default: 164 + throw new Exception("Object has unknown policy '{$policy}'!"); 165 + } 166 + 167 + return false; 105 168 } 106 169 107 - private function rejectObject($object, $policy) { 170 + private function rejectObject($object, $policy, $capability) { 108 171 if (!$this->raisePolicyExceptions) { 109 172 return; 110 173 } 111 174 112 - $message = "You do not have permission to view this object."; 175 + // TODO: clean this up 176 + $verb = $capability; 177 + 178 + $message = "You do not have permission to {$verb} this object."; 113 179 114 180 switch ($policy) { 115 181 case PhabricatorPolicies::POLICY_PUBLIC: 116 - $who = "This is curious, since anyone can view the object."; 182 + $who = "This is curious, since anyone can {$verb} the object."; 117 183 break; 118 184 case PhabricatorPolicies::POLICY_USER: 119 - $who = "To view this object, you must be logged in."; 185 + $who = "To {$verb} this object, you must be logged in."; 120 186 break; 121 187 case PhabricatorPolicies::POLICY_ADMIN: 122 - $who = "To view this object, you must be an administrator."; 188 + $who = "To {$verb} this object, you must be an administrator."; 123 189 break; 124 190 case PhabricatorPolicies::POLICY_NOONE: 125 - $who = "No one can view this object."; 191 + $who = "No one can {$verb} this object."; 126 192 break; 127 193 default: 128 - $who = "It is unclear who can view this object."; 194 + $who = "It is unclear who can {$verb} this object."; 129 195 break; 130 196 } 131 197
+18 -1
src/infrastructure/query/policy/PhabricatorPolicyQuery.php
··· 46 46 private $viewer; 47 47 private $raisePolicyExceptions; 48 48 private $rawResultLimit; 49 + private $capabilities; 49 50 50 51 51 52 /* -( Query Configuration )------------------------------------------------ */ ··· 74 75 */ 75 76 final public function getViewer() { 76 77 return $this->viewer; 78 + } 79 + 80 + 81 + /** 82 + * @task config 83 + */ 84 + final public function requireCapabilities(array $capabilities) { 85 + $this->capabilities = $capabilities; 86 + return $this; 77 87 } 78 88 79 89 ··· 145 155 146 156 $filter = new PhabricatorPolicyFilter(); 147 157 $filter->setViewer($this->viewer); 148 - $filter->setCapability(PhabricatorPolicyCapability::CAN_VIEW); 149 158 159 + if (!$this->capabilities) { 160 + $capabilities = array( 161 + PhabricatorPolicyCapability::CAN_VIEW, 162 + ); 163 + } else { 164 + $capabilities = $this->capabilities; 165 + } 166 + $filter->requireCapabilities($capabilities); 150 167 $filter->raisePolicyExceptions($this->raisePolicyExceptions); 151 168 152 169 $offset = (int)$this->getOffset();