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

Lift bulk tests for "many users against one object" capabilities into "PolicyFilterSet"

Summary:
Ref T13602. Currently, the policy framework can not execute "test if many users can see one object" particluarly efficiently. This test must be executed more broadly to implement the changes in T13602.

To avoid making this any worse than it already is, lift this block into a wrapper class that has a bulk queue + fetch API and could eventually be optimized.

Test Plan: Viewed a task with an `@mention` of a user without permission to see it in the summary, saw it rendered in a disabled style.

Maniphest Tasks: T13602

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

+131 -11
+2
src/__phutil_library_map__.php
··· 4270 4270 'PhabricatorPolicyExplainController' => 'applications/policy/controller/PhabricatorPolicyExplainController.php', 4271 4271 'PhabricatorPolicyFavoritesSetting' => 'applications/settings/setting/PhabricatorPolicyFavoritesSetting.php', 4272 4272 'PhabricatorPolicyFilter' => 'applications/policy/filter/PhabricatorPolicyFilter.php', 4273 + 'PhabricatorPolicyFilterSet' => 'applications/policy/filter/PhabricatorPolicyFilterSet.php', 4273 4274 'PhabricatorPolicyInterface' => 'applications/policy/interface/PhabricatorPolicyInterface.php', 4274 4275 'PhabricatorPolicyManagementShowWorkflow' => 'applications/policy/management/PhabricatorPolicyManagementShowWorkflow.php', 4275 4276 'PhabricatorPolicyManagementUnlockWorkflow' => 'applications/policy/management/PhabricatorPolicyManagementUnlockWorkflow.php', ··· 10920 10921 'PhabricatorPolicyExplainController' => 'PhabricatorPolicyController', 10921 10922 'PhabricatorPolicyFavoritesSetting' => 'PhabricatorInternalSetting', 10922 10923 'PhabricatorPolicyFilter' => 'Phobject', 10924 + 'PhabricatorPolicyFilterSet' => 'Phobject', 10923 10925 'PhabricatorPolicyInterface' => 'PhabricatorPHIDInterface', 10924 10926 'PhabricatorPolicyManagementShowWorkflow' => 'PhabricatorPolicyManagementWorkflow', 10925 10927 'PhabricatorPolicyManagementUnlockWorkflow' => 'PhabricatorPolicyManagementWorkflow',
+24 -11
src/applications/people/markup/PhabricatorMentionRemarkupRule.php
··· 87 87 $engine->setTextMetadata($mentioned_key, $mentioned); 88 88 $context_object = $engine->getConfig('contextObject'); 89 89 90 + $policy_object = null; 91 + if ($context_object) { 92 + if ($context_object instanceof PhabricatorPolicyInterface) { 93 + $policy_object = $context_object; 94 + } 95 + } 96 + 97 + if ($policy_object) { 98 + $policy_set = new PhabricatorPolicyFilterSet(); 99 + foreach ($actual_users as $user) { 100 + $policy_set->addCapability( 101 + $user, 102 + $policy_object, 103 + PhabricatorPolicyCapability::CAN_VIEW); 104 + } 105 + } 106 + 90 107 foreach ($metadata as $username => $tokens) { 91 108 $exists = isset($actual_users[$username]); 92 - $user_has_no_permission = false; 109 + $user_can_not_view = false; 93 110 94 111 if ($exists) { 95 112 $user = $actual_users[$username]; 96 113 Javelin::initBehavior('phui-hovercards'); 97 114 98 115 // Check if the user has view access to the object she was mentioned in 99 - if ($context_object 100 - && $context_object instanceof PhabricatorPolicyInterface) { 101 - if (!PhabricatorPolicyFilter::hasCapability( 116 + if ($policy_object) { 117 + $user_can_not_view = !$policy_set->hasCapability( 102 118 $user, 103 - $context_object, 104 - PhabricatorPolicyCapability::CAN_VIEW)) { 105 - // User mentioned has no permission to this object 106 - $user_has_no_permission = true; 107 - } 119 + $policy_object, 120 + PhabricatorPolicyCapability::CAN_VIEW); 108 121 } 109 122 110 123 $user_href = '/p/'.$user->getUserName().'/'; ··· 112 125 if ($engine->isHTMLMailMode()) { 113 126 $user_href = PhabricatorEnv::getProductionURI($user_href); 114 127 115 - if ($user_has_no_permission) { 128 + if ($user_can_not_view) { 116 129 $colors = ' 117 130 border-color: #92969D; 118 131 color: #92969D; ··· 146 159 ->setName('@'.$user->getUserName()) 147 160 ->setHref($user_href); 148 161 149 - if ($user_has_no_permission) { 162 + if ($user_can_not_view) { 150 163 $tag->addClass('phabricator-remarkup-mention-nopermission'); 151 164 } 152 165
+105
src/applications/policy/filter/PhabricatorPolicyFilterSet.php
··· 1 + <?php 2 + 3 + final class PhabricatorPolicyFilterSet 4 + extends Phobject { 5 + 6 + private $users = array(); 7 + private $objects = array(); 8 + 9 + private $capabilities = array(); 10 + private $queue = array(); 11 + private $results = array(); 12 + 13 + public function addCapability( 14 + PhabricatorUser $user, 15 + PhabricatorPolicyInterface $object, 16 + $capability) { 17 + 18 + $user_key = $this->getUserKey($user); 19 + $this->users[$user_key] = $user; 20 + 21 + $object_key = $this->getObjectKey($object); 22 + $this->objects[$object_key] = $object; 23 + 24 + if (!isset($this->capabilities[$capability][$user_key][$object_key])) { 25 + $this->capabilities[$capability][$user_key][$object_key] = true; 26 + $this->queue[$capability][$user_key][$object_key] = true; 27 + } 28 + 29 + return $this; 30 + } 31 + 32 + public function hasCapability( 33 + PhabricatorUser $user, 34 + PhabricatorPolicyInterface $object, 35 + $capability) { 36 + 37 + $user_key = $this->getUserKey($user); 38 + $this->users[$user_key] = $user; 39 + 40 + $object_key = $this->getObjectKey($object); 41 + $this->objects[$object_key] = $object; 42 + 43 + if (!isset($this->capabilities[$capability][$user_key][$object_key])) { 44 + throw new Exception( 45 + pht( 46 + 'Capability "%s" for user "%s" on object "%s" is being resolved, '. 47 + 'but was never queued with "addCapability()".', 48 + $capability, 49 + $user_key, 50 + $object_key)); 51 + } 52 + 53 + if (!isset($this->results[$capability][$user_key][$object_key])) { 54 + $this->resolveCapabilities(); 55 + } 56 + 57 + return $this->results[$capability][$user_key][$object_key]; 58 + } 59 + 60 + private function getUserKey(PhabricatorUser $user) { 61 + return $user->getCacheFragment(); 62 + } 63 + 64 + private function getObjectKey(PhabricatorPolicyInterface $object) { 65 + $object_phid = $object->getPHID(); 66 + 67 + if (!$object_phid) { 68 + throw new Exception( 69 + pht( 70 + 'Unable to perform capability tests on an object (of class "%s") '. 71 + 'with no PHID.', 72 + get_class($object))); 73 + } 74 + 75 + return $object_phid; 76 + } 77 + 78 + private function resolveCapabilities() { 79 + 80 + // This class is primarily used to test if a list of users (like 81 + // subscribers) can see a single object. It is not structured in a way 82 + // that makes this particularly efficient, and performance would probably 83 + // be improved if filtering supported this use case more narrowly. 84 + 85 + foreach ($this->queue as $capability => $user_map) { 86 + foreach ($user_map as $user_key => $object_map) { 87 + $user = $this->users[$user_key]; 88 + $objects = array_select_keys($this->objects, array_keys($object_map)); 89 + 90 + $filter = id(new PhabricatorPolicyFilter()) 91 + ->setViewer($user) 92 + ->requireCapabilities(array($capability)); 93 + $results = $filter->apply($objects); 94 + 95 + foreach ($object_map as $object_key => $object) { 96 + $has_capability = (bool)isset($results[$object_key]); 97 + $this->results[$capability][$user_key][$object_key] = $has_capability; 98 + } 99 + } 100 + } 101 + 102 + $this->queue = array(); 103 + } 104 + 105 + }