@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 Herald rules obey policies during application

Summary:
Ref T603. This closes the other major policy loophole in Herald, which was that you could write a rule like:

When [Always], [Add me to CC]

...and end up getting email about everything. These rules are now enforced:

- For a //personal// rule to trigger, you must be able to see the object, and you must be able to use the application the object exists in.
- In contrast, //global// rules will //always// trigger.

Also fixes some small bugs:

- Policy control access to thumbnails was overly restrictive.
- The Pholio and Maniphest Herald rules applied only the //last// "Add CC" or "Add Project" rules, since each rule overwrote previous rules.

Test Plan:
- Created "always cc me" herald and maniphest rules with a normal user.
- Created task with "user" visibility, saw CC.
- Created task with "no one" visibility, saw no CC and error message in transcript ("user can't see the object").
- Restricted Maniphest to administrators and created a task with "user" visibility. Same deal.
- Created "user" and "no one" mocks and saw CC and no CC, respectively.
- Thumbnail in Pholio worked properly.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T603

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

+84 -22
+9 -6
src/applications/files/controller/PhabricatorFileTransformController.php
··· 7 7 private $phid; 8 8 private $key; 9 9 10 + public function shouldRequireLogin() { 11 + return false; 12 + } 13 + 10 14 public function willProcessRequest(array $data) { 11 15 $this->transform = $data['transform']; 12 16 $this->phid = $data['phid']; 13 17 $this->key = $data['key']; 14 18 } 15 19 16 - public function shouldRequireLogin() { 17 - return false; 18 - } 19 - 20 20 public function processRequest() { 21 21 $viewer = $this->getRequest()->getUser(); 22 22 23 + // NOTE: This is a public/CDN endpoint, and permission to see files is 24 + // controlled by knowing the secret key, not by authentication. 25 + 23 26 $file = id(new PhabricatorFileQuery()) 24 - ->setViewer($viewer) 27 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 25 28 ->withPHIDs(array($this->phid)) 26 29 ->executeOne(); 27 30 if (!$file) { ··· 130 133 PhabricatorTransformedFile $xform) { 131 134 132 135 $file = id(new PhabricatorFileQuery()) 133 - ->setViewer($this->getRequest()->getUser()) 136 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 134 137 ->withPHIDs(array($xform->getTransformedPHID())) 135 138 ->executeOne(); 136 139 if (!$file) {
+1
src/applications/herald/adapter/HeraldAdapter.php
··· 114 114 115 115 abstract public function getAdapterContentName(); 116 116 abstract public function getAdapterApplicationClass(); 117 + abstract public function getObject(); 117 118 118 119 119 120 /* -( Fields )------------------------------------------------------------- */
+4
src/applications/herald/adapter/HeraldCommitAdapter.php
··· 31 31 return 'PhabricatorApplicationDiffusion'; 32 32 } 33 33 34 + public function getObject() { 35 + return $this->commit; 36 + } 37 + 34 38 public function getAdapterContentType() { 35 39 return 'commit'; 36 40 }
+4
src/applications/herald/adapter/HeraldDifferentialRevisionAdapter.php
··· 24 24 return 'PhabricatorApplicationDifferential'; 25 25 } 26 26 27 + public function getObject() { 28 + return $this->revision; 29 + } 30 + 27 31 public function getAdapterContentType() { 28 32 return 'differential'; 29 33 }
+6 -6
src/applications/herald/adapter/HeraldManiphestTaskAdapter.php
··· 22 22 return $this->task; 23 23 } 24 24 25 + public function getObject() { 26 + return $this->task; 27 + } 28 + 25 29 private function setCcPHIDs(array $cc_phids) { 26 30 $this->ccPHIDs = $cc_phids; 27 31 return $this; ··· 118 122 pht('Great success at doing nothing.')); 119 123 break; 120 124 case self::ACTION_ADD_CC: 121 - $add_cc = array(); 122 125 foreach ($effect->getTarget() as $phid) { 123 - $add_cc[$phid] = true; 126 + $this->ccPHIDs[] = $phid; 124 127 } 125 - $this->setCcPHIDs(array_keys($add_cc)); 126 128 $result[] = new HeraldApplyTranscript( 127 129 $effect, 128 130 true, ··· 143 145 pht('Assigned task.')); 144 146 break; 145 147 case self::ACTION_ADD_PROJECTS: 146 - $add_projects = array(); 147 148 foreach ($effect->getTarget() as $phid) { 148 - $add_projects[$phid] = true; 149 + $this->projectPHIDs[] = $phid; 149 150 } 150 - $this->setProjectPHIDs(array_keys($add_projects)); 151 151 $result[] = new HeraldApplyTranscript( 152 152 $effect, 153 153 true,
+5 -3
src/applications/herald/adapter/HeraldPholioMockAdapter.php
··· 12 12 return 'PhabricatorApplicationPholio'; 13 13 } 14 14 15 + public function getObject() { 16 + return $this->mock; 17 + } 18 + 15 19 public function setMock(PholioMock $mock) { 16 20 $this->mock = $mock; 17 21 return $this; ··· 97 101 pht('Great success at doing nothing.')); 98 102 break; 99 103 case self::ACTION_ADD_CC: 100 - $add_cc = array(); 101 104 foreach ($effect->getTarget() as $phid) { 102 - $add_cc[$phid] = true; 105 + $this->ccPHIDs[] = $phid; 103 106 } 104 - $this->setCcPHIDs(array_keys($add_cc)); 105 107 $result[] = new HeraldApplyTranscript( 106 108 $effect, 107 109 true,
+3 -2
src/applications/herald/controller/HeraldNewController.php
··· 49 49 HeraldRuleTypeConfig::RULE_TYPE_PERSONAL => 50 50 pht( 51 51 'Personal rules notify you about events. You own them, but they can '. 52 - 'only affect you.'), 52 + 'only affect you. Personal rules only trigger for objects you have '. 53 + 'permission to see.'), 53 54 HeraldRuleTypeConfig::RULE_TYPE_GLOBAL => 54 55 phutil_implode_html( 55 56 phutil_tag('br'), ··· 57 58 array( 58 59 pht( 59 60 'Global rules notify anyone about events. Global rules can '. 60 - 'bypass access control policies.'), 61 + 'bypass access control policies and act on any object.'), 61 62 $global_link, 62 63 ))), 63 64 );
+41 -5
src/applications/herald/engine/HeraldEngine.php
··· 233 233 234 234 $local_version = id(new HeraldRule())->getConfigVersion(); 235 235 if ($rule->getConfigVersion() > $local_version) { 236 - $reason = "Rule could not be processed, it was created with a newer ". 237 - "version of Herald."; 236 + $reason = pht( 237 + "Rule could not be processed, it was created with a newer version ". 238 + "of Herald."); 238 239 $result = false; 239 240 } else if (!$conditions) { 240 - $reason = "Rule failed automatically because it has no conditions."; 241 + $reason = pht( 242 + "Rule failed automatically because it has no conditions."); 241 243 $result = false; 242 244 } else if (!$rule->hasValidAuthor()) { 243 - $reason = "Rule failed automatically because its owner is invalid ". 244 - "or disabled."; 245 + $reason = pht( 246 + "Rule failed automatically because its owner is invalid ". 247 + "or disabled."); 248 + $result = false; 249 + } else if (!$this->canAuthorViewObject($rule, $object)) { 250 + $reason = pht( 251 + "Rule failed automatically because it is a personal rule and its ". 252 + "owner can not see the object."); 245 253 $result = false; 246 254 } else { 247 255 foreach ($conditions as $condition) { ··· 359 367 $effects[] = $effect; 360 368 } 361 369 return $effects; 370 + } 371 + 372 + private function canAuthorViewObject( 373 + HeraldRule $rule, 374 + HeraldAdapter $adapter) { 375 + 376 + // Authorship is irrelevant for global rules. 377 + if ($rule->isGlobalRule()) { 378 + return true; 379 + } 380 + 381 + // The author must be able to create rules for the adapter's content type. 382 + // In particular, this means that the application must be installed and 383 + // accessible to the user. For example, if a user writes a Differential 384 + // rule and then loses access to Differential, this disables the rule. 385 + $enabled = HeraldAdapter::getEnabledAdapterMap($rule->getAuthor()); 386 + if (empty($enabled[$adapter->getAdapterContentType()])) { 387 + return false; 388 + } 389 + 390 + // Finally, the author must be able to see the object itself. You can't 391 + // write a personal rule that CC's you on revisions you wouldn't otherwise 392 + // be able to see, for example. 393 + $object = $adapter->getObject(); 394 + return PhabricatorPolicyFilter::hasCapability( 395 + $rule->getAuthor(), 396 + $object, 397 + PhabricatorPolicyCapability::CAN_VIEW); 362 398 } 363 399 364 400 }
+1
src/applications/herald/query/HeraldRuleQuery.php
··· 211 211 } 212 212 213 213 $rule->attachValidAuthor(true); 214 + $rule->attachAuthor($users[$author_phid]); 214 215 } 215 216 } 216 217
+10
src/applications/herald/storage/HeraldRule.php
··· 17 17 18 18 private $ruleApplied = self::ATTACHABLE; // phids for which this rule has been applied 19 19 private $validAuthor = self::ATTACHABLE; 20 + private $author = self::ATTACHABLE; 20 21 private $conditions; 21 22 private $actions; 22 23 ··· 164 165 165 166 public function attachValidAuthor($valid) { 166 167 $this->validAuthor = $valid; 168 + return $this; 169 + } 170 + 171 + public function getAuthor() { 172 + return $this->assertAttached($this->author); 173 + } 174 + 175 + public function attachAuthor(PhabricatorUser $user) { 176 + $this->author = $user; 167 177 return $this; 168 178 } 169 179