1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-22 23:02:42 +01:00

Add a "members of all projects" (vs "...any project") custom policy rule to the upstream

Summary:
Ref T13151. See PHI702. An install is interested in a "members of all projects" (vs "members of any project", which is currently implemented) rule.

Although this is fairly niche, I think it's reasonable and doesn't have much of a maintenance cost.

This could already be implemented as an extension, but it would have to copy/paste a bunch of code.

Test Plan:
  - Ran unit tests.
  - Used the UI to select this policy for a task, with various values. Joined/left projects to satisfy/fail the rule. Behavior seemed correct.
  - Used the UI to select the existing policy rule ("any project"), joined/left projects to satisfy/fail the rule. Doesn't look like I broke anything.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13151

Differential Revision: https://secure.phabricator.com/D19486
This commit is contained in:
epriestley 2018-06-12 07:19:05 -07:00
parent 59b95f9397
commit cbff913432
5 changed files with 196 additions and 58 deletions

View file

@ -4026,7 +4026,9 @@ phutil_register_library_map(array(
'PhabricatorProjectWorkboardBackgroundTransaction' => 'applications/project/xaction/PhabricatorProjectWorkboardBackgroundTransaction.php', 'PhabricatorProjectWorkboardBackgroundTransaction' => 'applications/project/xaction/PhabricatorProjectWorkboardBackgroundTransaction.php',
'PhabricatorProjectWorkboardProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectWorkboardProfileMenuItem.php', 'PhabricatorProjectWorkboardProfileMenuItem' => 'applications/project/menuitem/PhabricatorProjectWorkboardProfileMenuItem.php',
'PhabricatorProjectWorkboardTransaction' => 'applications/project/xaction/PhabricatorProjectWorkboardTransaction.php', 'PhabricatorProjectWorkboardTransaction' => 'applications/project/xaction/PhabricatorProjectWorkboardTransaction.php',
'PhabricatorProjectsAllPolicyRule' => 'applications/project/policyrule/PhabricatorProjectsAllPolicyRule.php',
'PhabricatorProjectsAncestorsSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsAncestorsSearchEngineAttachment.php', 'PhabricatorProjectsAncestorsSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsAncestorsSearchEngineAttachment.php',
'PhabricatorProjectsBasePolicyRule' => 'applications/project/policyrule/PhabricatorProjectsBasePolicyRule.php',
'PhabricatorProjectsCurtainExtension' => 'applications/project/engineextension/PhabricatorProjectsCurtainExtension.php', 'PhabricatorProjectsCurtainExtension' => 'applications/project/engineextension/PhabricatorProjectsCurtainExtension.php',
'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php', 'PhabricatorProjectsEditEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsEditEngineExtension.php',
'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php', 'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php',
@ -9893,7 +9895,9 @@ phutil_register_library_map(array(
'PhabricatorProjectWorkboardBackgroundTransaction' => 'PhabricatorProjectTransactionType', 'PhabricatorProjectWorkboardBackgroundTransaction' => 'PhabricatorProjectTransactionType',
'PhabricatorProjectWorkboardProfileMenuItem' => 'PhabricatorProfileMenuItem', 'PhabricatorProjectWorkboardProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorProjectWorkboardTransaction' => 'PhabricatorProjectTransactionType', 'PhabricatorProjectWorkboardTransaction' => 'PhabricatorProjectTransactionType',
'PhabricatorProjectsAllPolicyRule' => 'PhabricatorProjectsBasePolicyRule',
'PhabricatorProjectsAncestorsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 'PhabricatorProjectsAncestorsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
'PhabricatorProjectsBasePolicyRule' => 'PhabricatorPolicyRule',
'PhabricatorProjectsCurtainExtension' => 'PHUICurtainExtension', 'PhabricatorProjectsCurtainExtension' => 'PHUICurtainExtension',
'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension', 'PhabricatorProjectsEditEngineExtension' => 'PhabricatorEditEngineExtension',
'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField', 'PhabricatorProjectsEditField' => 'PhabricatorTokenizerEditField',
@ -9902,7 +9906,7 @@ phutil_register_library_map(array(
'PhabricatorProjectsMailEngineExtension' => 'PhabricatorMailEngineExtension', 'PhabricatorProjectsMailEngineExtension' => 'PhabricatorMailEngineExtension',
'PhabricatorProjectsMembersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 'PhabricatorProjectsMembersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
'PhabricatorProjectsMembershipIndexEngineExtension' => 'PhabricatorIndexEngineExtension', 'PhabricatorProjectsMembershipIndexEngineExtension' => 'PhabricatorIndexEngineExtension',
'PhabricatorProjectsPolicyRule' => 'PhabricatorPolicyRule', 'PhabricatorProjectsPolicyRule' => 'PhabricatorProjectsBasePolicyRule',
'PhabricatorProjectsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 'PhabricatorProjectsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
'PhabricatorProjectsSearchEngineExtension' => 'PhabricatorSearchEngineExtension', 'PhabricatorProjectsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
'PhabricatorProjectsWatchersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 'PhabricatorProjectsWatchersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',

View file

@ -1177,6 +1177,100 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
$this->assertTrue($can_edit); $this->assertTrue($can_edit);
} }
public function testProjectPolicyRules() {
$author = $this->generateNewTestUser();
$proj_a = PhabricatorProject::initializeNewProject($author)
->setName('Policy A')
->save();
$proj_b = PhabricatorProject::initializeNewProject($author)
->setName('Policy B')
->save();
$user_none = $this->generateNewTestUser();
$user_any = $this->generateNewTestUser();
$user_all = $this->generateNewTestUser();
$this->joinProject($proj_a, $user_any);
$this->joinProject($proj_a, $user_all);
$this->joinProject($proj_b, $user_all);
$any_policy = id(new PhabricatorPolicy())
->setRules(
array(
array(
'action' => PhabricatorPolicy::ACTION_ALLOW,
'rule' => 'PhabricatorProjectsPolicyRule',
'value' => array(
$proj_a->getPHID(),
$proj_b->getPHID(),
),
),
))
->save();
$all_policy = id(new PhabricatorPolicy())
->setRules(
array(
array(
'action' => PhabricatorPolicy::ACTION_ALLOW,
'rule' => 'PhabricatorProjectsAllPolicyRule',
'value' => array(
$proj_a->getPHID(),
$proj_b->getPHID(),
),
),
))
->save();
$any_task = ManiphestTask::initializeNewTask($author)
->setViewPolicy($any_policy->getPHID())
->save();
$all_task = ManiphestTask::initializeNewTask($author)
->setViewPolicy($all_policy->getPHID())
->save();
$map = array(
array(
pht('Project policy rule; user in no projects'),
$user_none,
false,
false,
),
array(
pht('Project policy rule; user in some projects'),
$user_any,
true,
false,
),
array(
pht('Project policy rule; user in all projects'),
$user_all,
true,
true,
),
);
foreach ($map as $test_case) {
list($label, $user, $expect_any, $expect_all) = $test_case;
$can_any = PhabricatorPolicyFilter::hasCapability(
$user,
$any_task,
PhabricatorPolicyCapability::CAN_VIEW);
$can_all = PhabricatorPolicyFilter::hasCapability(
$user,
$all_task,
PhabricatorPolicyCapability::CAN_VIEW);
$this->assertEqual($expect_any, $can_any, pht('%s / Any', $label));
$this->assertEqual($expect_all, $can_all, pht('%s / All', $label));
}
}
private function moveToColumn( private function moveToColumn(
PhabricatorUser $viewer, PhabricatorUser $viewer,
PhabricatorProject $board, PhabricatorProject $board,

View file

@ -0,0 +1,29 @@
<?php
final class PhabricatorProjectsAllPolicyRule
extends PhabricatorProjectsBasePolicyRule {
public function getRuleDescription() {
return pht('members of all projects');
}
public function applyRule(
PhabricatorUser $viewer,
$value,
PhabricatorPolicyInterface $object) {
$memberships = $this->getMemberships($viewer->getPHID());
foreach ($value as $project_phid) {
if (empty($memberships[$project_phid])) {
return false;
}
}
return true;
}
public function getRuleOrder() {
return 205;
}
}

View file

@ -0,0 +1,64 @@
<?php
abstract class PhabricatorProjectsBasePolicyRule
extends PhabricatorPolicyRule {
private $memberships = array();
protected function getMemberships($viewer_phid) {
return idx($this->memberships, $viewer_phid, array());
}
public function willApplyRules(
PhabricatorUser $viewer,
array $values,
array $objects) {
$values = array_unique(array_filter(array_mergev($values)));
if (!$values) {
return;
}
$projects = id(new PhabricatorProjectQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withMemberPHIDs(array($viewer->getPHID()))
->withPHIDs($values)
->execute();
foreach ($projects as $project) {
$this->memberships[$viewer->getPHID()][$project->getPHID()] = true;
}
}
public function getValueControlType() {
return self::CONTROL_TYPE_TOKENIZER;
}
public function getValueControlTemplate() {
$datasource = id(new PhabricatorProjectDatasource())
->setParameters(
array(
'policy' => 1,
));
return $this->getDatasourceTemplate($datasource);
}
public function getValueForStorage($value) {
PhutilTypeSpec::newFromString('list<string>')->check($value);
return array_values($value);
}
public function getValueForDisplay(PhabricatorUser $viewer, $value) {
$handles = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs($value)
->execute();
return mpull($handles, 'getFullName', 'getPHID');
}
public function ruleHasEffect($value) {
return (bool)$value;
}
}

View file

@ -1,32 +1,10 @@
<?php <?php
final class PhabricatorProjectsPolicyRule final class PhabricatorProjectsPolicyRule
extends PhabricatorPolicyRule { extends PhabricatorProjectsBasePolicyRule {
private $memberships = array();
public function getRuleDescription() { public function getRuleDescription() {
return pht('members of projects'); return pht('members of any project');
}
public function willApplyRules(
PhabricatorUser $viewer,
array $values,
array $objects) {
$values = array_unique(array_filter(array_mergev($values)));
if (!$values) {
return;
}
$projects = id(new PhabricatorProjectQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withMemberPHIDs(array($viewer->getPHID()))
->withPHIDs($values)
->execute();
foreach ($projects as $project) {
$this->memberships[$viewer->getPHID()][$project->getPHID()] = true;
}
} }
public function applyRule( public function applyRule(
@ -34,8 +12,9 @@ final class PhabricatorProjectsPolicyRule
$value, $value,
PhabricatorPolicyInterface $object) { PhabricatorPolicyInterface $object) {
$memberships = $this->getMemberships($viewer->getPHID());
foreach ($value as $project_phid) { foreach ($value as $project_phid) {
if (isset($this->memberships[$viewer->getPHID()][$project_phid])) { if (isset($memberships[$project_phid])) {
return true; return true;
} }
} }
@ -43,40 +22,8 @@ final class PhabricatorProjectsPolicyRule
return false; return false;
} }
public function getValueControlType() {
return self::CONTROL_TYPE_TOKENIZER;
}
public function getValueControlTemplate() {
$datasource = id(new PhabricatorProjectDatasource())
->setParameters(
array(
'policy' => 1,
));
return $this->getDatasourceTemplate($datasource);
}
public function getRuleOrder() { public function getRuleOrder() {
return 200; return 200;
} }
public function getValueForStorage($value) {
PhutilTypeSpec::newFromString('list<string>')->check($value);
return array_values($value);
}
public function getValueForDisplay(PhabricatorUser $viewer, $value) {
$handles = id(new PhabricatorHandleQuery())
->setViewer($viewer)
->withPHIDs($value)
->execute();
return mpull($handles, 'getFullName', 'getPHID');
}
public function ruleHasEffect($value) {
return (bool)$value;
}
} }