1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-24 14:30:56 +01:00

Make the new Build Plan "Runnable" behavior work

Summary:
Ref T13258. Fixes T11415. This makes "Runnable" actually do something:

  - With "Runnable" set to "If Editable" (default): to manually run, pause, resume, abort, or restart a build, you must normally be able to edit the associated build plan.
  - If you toggle "Runnable" to "If Viewable", anyone who can view the build plan may take these actions.

This is pretty straightforward since T9614 already got us pretty close to this ruleset a while ago.

Test Plan:
  - Created a Build Plan, set "Can Edit" to just me, toggled "Runnable" to "If Viewable"/"If Editable", tried to take actions as another user.
  - With "If Editable", unable to run, pause, resume, abort, or restart as another user.
  - With "If Viewable", those actions work.

Reviewers: amckinley

Reviewed By: amckinley

Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam

Maniphest Tasks: T13258, T11415

Differential Revision: https://secure.phabricator.com/D20229
This commit is contained in:
epriestley 2019-02-28 11:17:04 -08:00
parent 983cf885e7
commit ee0ad4703e
7 changed files with 115 additions and 18 deletions

View file

@ -1340,6 +1340,7 @@ phutil_register_library_map(array(
'HarbormasterBuildPlanNameNgrams' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlanNameNgrams.php', 'HarbormasterBuildPlanNameNgrams' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlanNameNgrams.php',
'HarbormasterBuildPlanNameTransaction' => 'applications/harbormaster/xaction/plan/HarbormasterBuildPlanNameTransaction.php', 'HarbormasterBuildPlanNameTransaction' => 'applications/harbormaster/xaction/plan/HarbormasterBuildPlanNameTransaction.php',
'HarbormasterBuildPlanPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildPlanPHIDType.php', 'HarbormasterBuildPlanPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildPlanPHIDType.php',
'HarbormasterBuildPlanPolicyCodex' => 'applications/harbormaster/codex/HarbormasterBuildPlanPolicyCodex.php',
'HarbormasterBuildPlanQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanQuery.php', 'HarbormasterBuildPlanQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanQuery.php',
'HarbormasterBuildPlanSearchAPIMethod' => 'applications/harbormaster/conduit/HarbormasterBuildPlanSearchAPIMethod.php', 'HarbormasterBuildPlanSearchAPIMethod' => 'applications/harbormaster/conduit/HarbormasterBuildPlanSearchAPIMethod.php',
'HarbormasterBuildPlanSearchEngine' => 'applications/harbormaster/query/HarbormasterBuildPlanSearchEngine.php', 'HarbormasterBuildPlanSearchEngine' => 'applications/harbormaster/query/HarbormasterBuildPlanSearchEngine.php',
@ -6945,6 +6946,7 @@ phutil_register_library_map(array(
'PhabricatorNgramsInterface', 'PhabricatorNgramsInterface',
'PhabricatorConduitResultInterface', 'PhabricatorConduitResultInterface',
'PhabricatorProjectInterface', 'PhabricatorProjectInterface',
'PhabricatorPolicyCodexInterface',
), ),
'HarbormasterBuildPlanBehavior' => 'Phobject', 'HarbormasterBuildPlanBehavior' => 'Phobject',
'HarbormasterBuildPlanBehaviorOption' => 'Phobject', 'HarbormasterBuildPlanBehaviorOption' => 'Phobject',
@ -6958,6 +6960,7 @@ phutil_register_library_map(array(
'HarbormasterBuildPlanNameNgrams' => 'PhabricatorSearchNgrams', 'HarbormasterBuildPlanNameNgrams' => 'PhabricatorSearchNgrams',
'HarbormasterBuildPlanNameTransaction' => 'HarbormasterBuildPlanTransactionType', 'HarbormasterBuildPlanNameTransaction' => 'HarbormasterBuildPlanTransactionType',
'HarbormasterBuildPlanPHIDType' => 'PhabricatorPHIDType', 'HarbormasterBuildPlanPHIDType' => 'PhabricatorPHIDType',
'HarbormasterBuildPlanPolicyCodex' => 'PhabricatorPolicyCodex',
'HarbormasterBuildPlanQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'HarbormasterBuildPlanQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'HarbormasterBuildPlanSearchAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'HarbormasterBuildPlanSearchAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'HarbormasterBuildPlanSearchEngine' => 'PhabricatorApplicationSearchEngine', 'HarbormasterBuildPlanSearchEngine' => 'PhabricatorApplicationSearchEngine',

View file

@ -0,0 +1,38 @@
<?php
final class HarbormasterBuildPlanPolicyCodex
extends PhabricatorPolicyCodex {
public function getPolicySpecialRuleDescriptions() {
$object = $this->getObject();
$run_with_view = $object->canRunWithoutEditCapability();
$rules = array();
$rules[] = $this->newRule()
->setCapabilities(
array(
PhabricatorPolicyCapability::CAN_EDIT,
))
->setIsActive(!$run_with_view)
->setDescription(
pht(
'You must have edit permission on this build plan to pause, '.
'abort, resume, or restart it.'));
$rules[] = $this->newRule()
->setCapabilities(
array(
PhabricatorPolicyCapability::CAN_EDIT,
))
->setIsActive(!$run_with_view)
->setDescription(
pht(
'You must have edit permission on this build plan to run it '.
'manually.'));
return $rules;
}
}

View file

@ -9,16 +9,13 @@ final class HarbormasterPlanRunController extends HarbormasterPlanController {
$plan = id(new HarbormasterBuildPlanQuery()) $plan = id(new HarbormasterBuildPlanQuery())
->setViewer($viewer) ->setViewer($viewer)
->withIDs(array($plan_id)) ->withIDs(array($plan_id))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne(); ->executeOne();
if (!$plan) { if (!$plan) {
return new Aphront404Response(); return new Aphront404Response();
} }
$plan->assertHasRunCapability($viewer);
$cancel_uri = $this->getApplicationURI("plan/{$plan_id}/"); $cancel_uri = $this->getApplicationURI("plan/{$plan_id}/");
if (!$plan->canRunManually()) { if (!$plan->canRunManually()) {

View file

@ -266,7 +266,7 @@ final class HarbormasterPlanViewController extends HarbormasterPlanController {
->setIcon('fa-ban')); ->setIcon('fa-ban'));
} }
$can_run = ($can_edit && $plan->canRunManually()); $can_run = ($plan->hasRunCapability($viewer) && $plan->canRunManually());
$curtain->addAction( $curtain->addAction(
id(new PhabricatorActionView()) id(new PhabricatorActionView())

View file

@ -9,6 +9,10 @@ final class HarbormasterBuildPlanBehavior
private $defaultKey; private $defaultKey;
private $editInstructions; private $editInstructions;
const BEHAVIOR_RUNNABLE = 'runnable';
const RUNNABLE_IF_VIEWABLE = 'view';
const RUNNABLE_IF_EDITABLE = 'edit';
public function setKey($key) { public function setKey($key) {
$this->key = $key; $this->key = $key;
return $this; return $this;
@ -114,6 +118,19 @@ final class HarbormasterBuildPlanBehavior
return sprintf('behavior.%s', $behavior_key); return sprintf('behavior.%s', $behavior_key);
} }
public static function getBehavior($key) {
$behaviors = self::newPlanBehaviors();
if (!isset($behaviors[$key])) {
throw new Exception(
pht(
'No build plan behavior with key "%s" exists.',
$key));
}
return $behaviors[$key];
}
public static function newPlanBehaviors() { public static function newPlanBehaviors() {
$draft_options = array( $draft_options = array(
id(new HarbormasterBuildPlanBehaviorOption()) id(new HarbormasterBuildPlanBehaviorOption())
@ -224,14 +241,14 @@ final class HarbormasterBuildPlanBehavior
$run_options = array( $run_options = array(
id(new HarbormasterBuildPlanBehaviorOption()) id(new HarbormasterBuildPlanBehaviorOption())
->setKey('edit') ->setKey(self::RUNNABLE_IF_EDITABLE)
->setIcon('fa-pencil green') ->setIcon('fa-pencil green')
->setName(pht('If Editable')) ->setName(pht('If Editable'))
->setIsDefault(true) ->setIsDefault(true)
->setDescription( ->setDescription(
pht('Only users who can edit the plan can run it manually.')), pht('Only users who can edit the plan can run it manually.')),
id(new HarbormasterBuildPlanBehaviorOption()) id(new HarbormasterBuildPlanBehaviorOption())
->setKey('view') ->setKey(self::RUNNABLE_IF_VIEWABLE)
->setIcon('fa-exclamation-triangle yellow') ->setIcon('fa-exclamation-triangle yellow')
->setName(pht('If Viewable')) ->setName(pht('If Viewable'))
->setDescription( ->setDescription(
@ -315,7 +332,7 @@ final class HarbormasterBuildPlanBehavior
->setName(pht('Restartable')) ->setName(pht('Restartable'))
->setOptions($restart_options), ->setOptions($restart_options),
id(new self()) id(new self())
->setKey('runnable') ->setKey(self::BEHAVIOR_RUNNABLE)
->setEditInstructions( ->setEditInstructions(
pht( pht(
'To run a build manually, you normally must have permission to '. 'To run a build manually, you normally must have permission to '.
@ -323,9 +340,8 @@ final class HarbormasterBuildPlanBehavior
'can see the build plan be able to run and restart the build, you '. 'can see the build plan be able to run and restart the build, you '.
'can change the behavior here.'. 'can change the behavior here.'.
"\n\n". "\n\n".
'Note that this affects both {nav Run Plan Manually} and '. 'Note that this controls access to all build management actions: '.
'{nav Restart Build}, since the two actions are largely '. '"Run Plan Manually", "Restart", "Abort", "Pause", and "Resume".'.
'equivalent.'.
"\n\n". "\n\n".
'WARNING: This may be unsafe, particularly if the build has '. 'WARNING: This may be unsafe, particularly if the build has '.
'side effects like deployment.'. 'side effects like deployment.'.

View file

@ -334,14 +334,17 @@ final class HarbormasterBuild extends HarbormasterDAO
} }
public function assertCanIssueCommand(PhabricatorUser $viewer, $command) { public function assertCanIssueCommand(PhabricatorUser $viewer, $command) {
$need_edit = false; $plan = $this->getBuildPlan();
$need_edit = true;
switch ($command) { switch ($command) {
case HarbormasterBuildCommand::COMMAND_RESTART: case HarbormasterBuildCommand::COMMAND_RESTART:
break;
case HarbormasterBuildCommand::COMMAND_PAUSE: case HarbormasterBuildCommand::COMMAND_PAUSE:
case HarbormasterBuildCommand::COMMAND_RESUME: case HarbormasterBuildCommand::COMMAND_RESUME:
case HarbormasterBuildCommand::COMMAND_ABORT: case HarbormasterBuildCommand::COMMAND_ABORT:
$need_edit = true; if ($plan->canRunWithoutEditCapability()) {
$need_edit = false;
}
break; break;
default: default:
throw new Exception( throw new Exception(
@ -355,7 +358,7 @@ final class HarbormasterBuild extends HarbormasterDAO
if ($need_edit) { if ($need_edit) {
PhabricatorPolicyFilter::requireCapability( PhabricatorPolicyFilter::requireCapability(
$viewer, $viewer,
$this->getBuildPlan(), $plan,
PhabricatorPolicyCapability::CAN_EDIT); PhabricatorPolicyCapability::CAN_EDIT);
} }
} }

View file

@ -10,7 +10,8 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
PhabricatorSubscribableInterface, PhabricatorSubscribableInterface,
PhabricatorNgramsInterface, PhabricatorNgramsInterface,
PhabricatorConduitResultInterface, PhabricatorConduitResultInterface,
PhabricatorProjectInterface { PhabricatorProjectInterface,
PhabricatorPolicyCodexInterface {
protected $name; protected $name;
protected $planStatus; protected $planStatus;
@ -133,7 +134,6 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
return true; return true;
} }
public function getName() { public function getName() {
$autoplan = $this->getAutoplan(); $autoplan = $this->getAutoplan();
if ($autoplan) { if ($autoplan) {
@ -143,6 +143,38 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
return parent::getName(); return parent::getName();
} }
public function hasRunCapability(PhabricatorUser $viewer) {
try {
$this->assertHasRunCapability($viewer);
return true;
} catch (PhabricatorPolicyException $ex) {
return false;
}
}
public function canRunWithoutEditCapability() {
$runnable = HarbormasterBuildPlanBehavior::BEHAVIOR_RUNNABLE;
$if_viewable = HarbormasterBuildPlanBehavior::RUNNABLE_IF_VIEWABLE;
$option = HarbormasterBuildPlanBehavior::getBehavior($runnable)
->getPlanOption($this);
return ($option->getKey() === $if_viewable);
}
public function assertHasRunCapability(PhabricatorUser $viewer) {
if ($this->canRunWithoutEditCapability()) {
$capability = PhabricatorPolicyCapability::CAN_VIEW;
} else {
$capability = PhabricatorPolicyCapability::CAN_EDIT;
}
PhabricatorPolicyFilter::requireCapability(
$viewer,
$this,
$capability);
}
/* -( PhabricatorSubscribableInterface )----------------------------------- */ /* -( PhabricatorSubscribableInterface )----------------------------------- */
@ -265,4 +297,12 @@ final class HarbormasterBuildPlan extends HarbormasterDAO
return array(); return array();
} }
/* -( PhabricatorPolicyCodexInterface )------------------------------------ */
public function newPolicyCodex() {
return new HarbormasterBuildPlanPolicyCodex();
}
} }