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

Give Harbormaster Build Plans real policies

Summary:
Ref T9614. Currently, a lot of Build Plan behavior is covered by a global "can manage" policy.

One install in particular is experiencing difficulty with warring factions within engineering aborting one another's builds.

As a first step to remedy this, and also generally make Harbormaster more flexible and bring it in line with other applications in terms of policy power:

- Give Build Plans normal view/edit policies.
- Require "Can Edit" to run a plan manually.

Having "Can View" on plans may be a little weird in some cases (the status of a Buildable might be bad because of a build you can't see) but we can cross that bridge when we come to it.

Next change here will require "Can Edit" to abort a build. This will reasonably allow installs to reserve pause/abort for administrators/adults. (I might let anyone restart a plan, though?)

Test Plan:
- Created a new build plan.
- Verified defaults were inherited from application defaults (swapped them around, too).
- Saved build plan.
- Edited policies.
- Verified autoplans get the right policies.
- Verified old plans got migrated properly.
- Tried to run a plan I couldn't edit (denied).
- Ran a plan from CLI with `bin/harbormaster`.
- Tried to create a plan with an unprivileged user.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9614

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

+163 -74
+5
resources/sql/autopatches/20151023.harborpolicy.1.sql
··· 1 + ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildplan 2 + ADD viewPolicy VARBINARY(64) NOT NULL; 3 + 4 + ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildplan 5 + ADD editPolicy VARBINARY(64) NOT NULL;
+21
resources/sql/autopatches/20151023.harborpolicy.2.php
··· 1 + <?php 2 + 3 + $table = new HarbormasterBuildPlan(); 4 + $conn_w = $table->establishConnection('w'); 5 + 6 + $view_policy = PhabricatorPolicies::getMostOpenPolicy(); 7 + queryfx( 8 + $conn_w, 9 + 'UPDATE %T SET viewPolicy = %s WHERE viewPolicy = %s', 10 + $table->getTableName(), 11 + $view_policy, 12 + ''); 13 + 14 + $edit_policy = id(new PhabricatorHarbormasterApplication()) 15 + ->getPolicy(HarbormasterCreatePlansCapability::CAPABILITY); 16 + queryfx( 17 + $conn_w, 18 + 'UPDATE %T SET editPolicy = %s WHERE editPolicy = %s', 19 + $table->getTableName(), 20 + $edit_policy, 21 + '');
+6 -2
src/__phutil_library_map__.php
··· 991 991 'HarbormasterBuildPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildPHIDType.php', 992 992 'HarbormasterBuildPlan' => 'applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php', 993 993 'HarbormasterBuildPlanDatasource' => 'applications/harbormaster/typeahead/HarbormasterBuildPlanDatasource.php', 994 + 'HarbormasterBuildPlanDefaultEditCapability' => 'applications/harbormaster/capability/HarbormasterBuildPlanDefaultEditCapability.php', 995 + 'HarbormasterBuildPlanDefaultViewCapability' => 'applications/harbormaster/capability/HarbormasterBuildPlanDefaultViewCapability.php', 994 996 'HarbormasterBuildPlanEditor' => 'applications/harbormaster/editor/HarbormasterBuildPlanEditor.php', 995 997 'HarbormasterBuildPlanPHIDType' => 'applications/harbormaster/phid/HarbormasterBuildPlanPHIDType.php', 996 998 'HarbormasterBuildPlanQuery' => 'applications/harbormaster/query/HarbormasterBuildPlanQuery.php', ··· 1036 1038 'HarbormasterConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterConduitAPIMethod.php', 1037 1039 'HarbormasterController' => 'applications/harbormaster/controller/HarbormasterController.php', 1038 1040 'HarbormasterCreateArtifactConduitAPIMethod' => 'applications/harbormaster/conduit/HarbormasterCreateArtifactConduitAPIMethod.php', 1041 + 'HarbormasterCreatePlansCapability' => 'applications/harbormaster/capability/HarbormasterCreatePlansCapability.php', 1039 1042 'HarbormasterDAO' => 'applications/harbormaster/storage/HarbormasterDAO.php', 1040 1043 'HarbormasterDrydockBuildStepGroup' => 'applications/harbormaster/stepgroup/HarbormasterDrydockBuildStepGroup.php', 1041 1044 'HarbormasterDrydockCommandBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterDrydockCommandBuildStepImplementation.php', ··· 1049 1052 'HarbormasterLeaseWorkingCopyBuildStepImplementation' => 'applications/harbormaster/step/HarbormasterLeaseWorkingCopyBuildStepImplementation.php', 1050 1053 'HarbormasterLintMessagesController' => 'applications/harbormaster/controller/HarbormasterLintMessagesController.php', 1051 1054 'HarbormasterLintPropertyView' => 'applications/harbormaster/view/HarbormasterLintPropertyView.php', 1052 - 'HarbormasterManagePlansCapability' => 'applications/harbormaster/capability/HarbormasterManagePlansCapability.php', 1053 1055 'HarbormasterManagementBuildWorkflow' => 'applications/harbormaster/management/HarbormasterManagementBuildWorkflow.php', 1054 1056 'HarbormasterManagementUpdateWorkflow' => 'applications/harbormaster/management/HarbormasterManagementUpdateWorkflow.php', 1055 1057 'HarbormasterManagementWorkflow' => 'applications/harbormaster/management/HarbormasterManagementWorkflow.php', ··· 4816 4818 'PhabricatorSubscribableInterface', 4817 4819 ), 4818 4820 'HarbormasterBuildPlanDatasource' => 'PhabricatorTypeaheadDatasource', 4821 + 'HarbormasterBuildPlanDefaultEditCapability' => 'PhabricatorPolicyCapability', 4822 + 'HarbormasterBuildPlanDefaultViewCapability' => 'PhabricatorPolicyCapability', 4819 4823 'HarbormasterBuildPlanEditor' => 'PhabricatorApplicationTransactionEditor', 4820 4824 'HarbormasterBuildPlanPHIDType' => 'PhabricatorPHIDType', 4821 4825 'HarbormasterBuildPlanQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', ··· 4875 4879 'HarbormasterConduitAPIMethod' => 'ConduitAPIMethod', 4876 4880 'HarbormasterController' => 'PhabricatorController', 4877 4881 'HarbormasterCreateArtifactConduitAPIMethod' => 'HarbormasterConduitAPIMethod', 4882 + 'HarbormasterCreatePlansCapability' => 'PhabricatorPolicyCapability', 4878 4883 'HarbormasterDAO' => 'PhabricatorLiskDAO', 4879 4884 'HarbormasterDrydockBuildStepGroup' => 'HarbormasterBuildStepGroup', 4880 4885 'HarbormasterDrydockCommandBuildStepImplementation' => 'HarbormasterBuildStepImplementation', ··· 4888 4893 'HarbormasterLeaseWorkingCopyBuildStepImplementation' => 'HarbormasterBuildStepImplementation', 4889 4894 'HarbormasterLintMessagesController' => 'HarbormasterController', 4890 4895 'HarbormasterLintPropertyView' => 'AphrontView', 4891 - 'HarbormasterManagePlansCapability' => 'PhabricatorPolicyCapability', 4892 4896 'HarbormasterManagementBuildWorkflow' => 'HarbormasterManagementWorkflow', 4893 4897 'HarbormasterManagementUpdateWorkflow' => 'HarbormasterManagementWorkflow', 4894 4898 'HarbormasterManagementWorkflow' => 'PhabricatorManagementWorkflow',
+10 -2
src/applications/harbormaster/application/PhabricatorHarbormasterApplication.php
··· 95 95 96 96 protected function getCustomCapabilities() { 97 97 return array( 98 - HarbormasterManagePlansCapability::CAPABILITY => array( 99 - 'caption' => pht('Can create and manage build plans.'), 98 + HarbormasterCreatePlansCapability::CAPABILITY => array( 99 + 'default' => PhabricatorPolicies::POLICY_ADMIN, 100 + ), 101 + HarbormasterBuildPlanDefaultViewCapability::CAPABILITY => array( 102 + 'template' => HarbormasterBuildPlanPHIDType::TYPECONST, 103 + 'capability' => PhabricatorPolicyCapability::CAN_VIEW, 104 + ), 105 + HarbormasterBuildPlanDefaultEditCapability::CAPABILITY => array( 106 + 'template' => HarbormasterBuildPlanPHIDType::TYPECONST, 107 + 'capability' => PhabricatorPolicyCapability::CAN_EDIT, 100 108 'default' => PhabricatorPolicies::POLICY_ADMIN, 101 109 ), 102 110 );
+12
src/applications/harbormaster/capability/HarbormasterBuildPlanDefaultEditCapability.php
··· 1 + <?php 2 + 3 + final class HarbormasterBuildPlanDefaultEditCapability 4 + extends PhabricatorPolicyCapability { 5 + 6 + const CAPABILITY = 'harbormaster.plan.default.edit'; 7 + 8 + public function getCapabilityName() { 9 + return pht('Default Build Plan Edit Policy'); 10 + } 11 + 12 + }
+16
src/applications/harbormaster/capability/HarbormasterBuildPlanDefaultViewCapability.php
··· 1 + <?php 2 + 3 + final class HarbormasterBuildPlanDefaultViewCapability 4 + extends PhabricatorPolicyCapability { 5 + 6 + const CAPABILITY = 'harbomaster.plan.default.view'; 7 + 8 + public function getCapabilityName() { 9 + return pht('Default Build Plan View Policy'); 10 + } 11 + 12 + public function shouldAllowPublicPolicySetting() { 13 + return true; 14 + } 15 + 16 + }
+17
src/applications/harbormaster/capability/HarbormasterCreatePlansCapability.php
··· 1 + <?php 2 + 3 + final class HarbormasterCreatePlansCapability 4 + extends PhabricatorPolicyCapability { 5 + 6 + const CAPABILITY = 'harbormaster.plans'; 7 + 8 + public function getCapabilityName() { 9 + return pht('Can Create Build Plans'); 10 + } 11 + 12 + public function describeCapabilityRejection() { 13 + return pht( 14 + 'You do not have permission to create Harbormaster build plans.'); 15 + } 16 + 17 + }
-17
src/applications/harbormaster/capability/HarbormasterManagePlansCapability.php
··· 1 - <?php 2 - 3 - final class HarbormasterManagePlansCapability 4 - extends PhabricatorPolicyCapability { 5 - 6 - const CAPABILITY = 'harbormaster.plans'; 7 - 8 - public function getCapabilityName() { 9 - return pht('Can Manage Build Plans'); 10 - } 11 - 12 - public function describeCapabilityRejection() { 13 - return pht( 14 - 'You do not have permission to manage Harbormaster build plans.'); 15 - } 16 - 17 - }
-3
src/applications/harbormaster/controller/HarbormasterPlanDisableController.php
··· 6 6 public function handleRequest(AphrontRequest $request) { 7 7 $viewer = $this->getViewer(); 8 8 9 - $this->requireApplicationCapability( 10 - HarbormasterManagePlansCapability::CAPABILITY); 11 - 12 9 $plan = id(new HarbormasterBuildPlanQuery()) 13 10 ->setViewer($viewer) 14 11 ->withIDs(array($request->getURIData('id')))
+44 -10
src/applications/harbormaster/controller/HarbormasterPlanEditController.php
··· 5 5 public function handleRequest(AphrontRequest $request) { 6 6 $viewer = $this->getViewer(); 7 7 8 - $this->requireApplicationCapability( 9 - HarbormasterManagePlansCapability::CAPABILITY); 10 - 11 8 $id = $request->getURIData('id'); 12 9 if ($id) { 13 10 $plan = id(new HarbormasterBuildPlanQuery()) ··· 23 20 return new Aphront404Response(); 24 21 } 25 22 } else { 23 + $this->requireApplicationCapability( 24 + HarbormasterCreatePlansCapability::CAPABILITY); 25 + 26 26 $plan = HarbormasterBuildPlan::initializeNewBuildPlan($viewer); 27 27 } 28 28 29 29 $e_name = true; 30 30 $v_name = $plan->getName(); 31 + $v_view = $plan->getViewPolicy(); 32 + $v_edit = $plan->getEditPolicy(); 31 33 $validation_exception = null; 32 34 if ($request->isFormPost()) { 33 35 $xactions = array(); 34 36 35 37 $v_name = $request->getStr('name'); 38 + $v_view = $request->getStr('viewPolicy'); 39 + $v_edit = $request->getStr('editPolicy'); 40 + 36 41 $e_name = null; 42 + 37 43 $type_name = HarbormasterBuildPlanTransaction::TYPE_NAME; 44 + $type_view = PhabricatorTransactions::TYPE_VIEW_POLICY; 45 + $type_edit = PhabricatorTransactions::TYPE_EDIT_POLICY; 38 46 39 47 $xactions[] = id(new HarbormasterBuildPlanTransaction()) 40 48 ->setTransactionType($type_name) 41 49 ->setNewValue($v_name); 42 50 51 + $xactions[] = id(new HarbormasterBuildPlanTransaction()) 52 + ->setTransactionType($type_view) 53 + ->setNewValue($v_view); 54 + 55 + $xactions[] = id(new HarbormasterBuildPlanTransaction()) 56 + ->setTransactionType($type_edit) 57 + ->setNewValue($v_edit); 58 + 43 59 $editor = id(new HarbormasterBuildPlanEditor()) 44 60 ->setActor($viewer) 45 61 ->setContinueOnNoEffect(true) ··· 70 86 $cancel_uri = $this->getApplicationURI('plan/'.$plan->getID().'/'); 71 87 $save_button = pht('Save Build Plan'); 72 88 } 89 + 90 + $policies = id(new PhabricatorPolicyQuery()) 91 + ->setViewer($viewer) 92 + ->setObject($plan) 93 + ->execute(); 73 94 74 95 $form = id(new AphrontFormView()) 75 96 ->setUser($viewer) 76 - ->appendChild( 97 + ->appendControl( 77 98 id(new AphrontFormTextControl()) 78 99 ->setLabel(pht('Plan Name')) 79 100 ->setName('name') 80 101 ->setError($e_name) 81 - ->setValue($v_name)); 82 - 83 - $form->appendChild( 84 - id(new AphrontFormSubmitControl()) 85 - ->setValue($save_button) 86 - ->addCancelButton($cancel_uri)); 102 + ->setValue($v_name)) 103 + ->appendControl( 104 + id(new AphrontFormPolicyControl()) 105 + ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) 106 + ->setPolicyObject($plan) 107 + ->setPolicies($policies) 108 + ->setValue($v_view) 109 + ->setName('viewPolicy')) 110 + ->appendControl( 111 + id(new AphrontFormPolicyControl()) 112 + ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) 113 + ->setPolicyObject($plan) 114 + ->setPolicies($policies) 115 + ->setValue($v_edit) 116 + ->setName('editPolicy')) 117 + ->appendControl( 118 + id(new AphrontFormSubmitControl()) 119 + ->setValue($save_button) 120 + ->addCancelButton($cancel_uri)); 87 121 88 122 $box = id(new PHUIObjectBoxView()) 89 123 ->setHeaderText($title)
+1 -1
src/applications/harbormaster/controller/HarbormasterPlanListController.php
··· 42 42 $crumbs = parent::buildApplicationCrumbs(); 43 43 44 44 $can_create = $this->hasApplicationCapability( 45 - HarbormasterManagePlansCapability::CAPABILITY); 45 + HarbormasterCreatePlansCapability::CAPABILITY); 46 46 47 47 $crumbs->addAction( 48 48 id(new PHUIListItemView())
+5 -8
src/applications/harbormaster/controller/HarbormasterPlanRunController.php
··· 4 4 5 5 public function handleRequest(AphrontRequest $request) { 6 6 $viewer = $this->getViewer(); 7 - 8 - $this->requireApplicationCapability( 9 - HarbormasterManagePlansCapability::CAPABILITY); 10 - 11 7 $plan_id = $request->getURIData('id'); 12 8 13 - // NOTE: At least for now, this only requires the "Can Manage Plans" 14 - // capability, not the "Can Edit" capability. Possibly it should have 15 - // a more stringent requirement, though. 16 - 17 9 $plan = id(new HarbormasterBuildPlanQuery()) 18 10 ->setViewer($viewer) 19 11 ->withIDs(array($plan_id)) 12 + ->requireCapabilities( 13 + array( 14 + PhabricatorPolicyCapability::CAN_VIEW, 15 + PhabricatorPolicyCapability::CAN_EDIT, 16 + )) 20 17 ->executeOne(); 21 18 if (!$plan) { 22 19 return new Aphront404Response();
+4 -14
src/applications/harbormaster/controller/HarbormasterPlanViewController.php
··· 3 3 final class HarbormasterPlanViewController extends HarbormasterPlanController { 4 4 5 5 public function handleRequest(AphrontRequest $request) { 6 - $viewer = $this->getviewer(); 6 + $viewer = $this->getViewer(); 7 7 $id = $request->getURIData('id'); 8 8 9 9 $plan = id(new HarbormasterBuildPlanQuery()) ··· 81 81 ->execute(); 82 82 $steps = mpull($steps, null, 'getPHID'); 83 83 84 - $has_manage = $this->hasApplicationCapability( 85 - HarbormasterManagePlansCapability::CAPABILITY); 86 - 87 - $has_edit = PhabricatorPolicyFilter::hasCapability( 84 + $can_edit = PhabricatorPolicyFilter::hasCapability( 88 85 $viewer, 89 86 $plan, 90 87 PhabricatorPolicyCapability::CAN_EDIT); 91 - 92 - $can_edit = ($has_manage && $has_edit); 93 88 94 89 $step_list = id(new PHUIObjectItemListView()) 95 90 ->setUser($viewer) ··· 252 247 ->setObject($plan) 253 248 ->setObjectURI($this->getApplicationURI("plan/{$id}/")); 254 249 255 - $has_manage = $this->hasApplicationCapability( 256 - HarbormasterManagePlansCapability::CAPABILITY); 257 - 258 - $has_edit = PhabricatorPolicyFilter::hasCapability( 250 + $can_edit = PhabricatorPolicyFilter::hasCapability( 259 251 $viewer, 260 252 $plan, 261 253 PhabricatorPolicyCapability::CAN_EDIT); 262 - 263 - $can_edit = ($has_manage && $has_edit); 264 254 265 255 $list->addAction( 266 256 id(new PhabricatorActionView()) ··· 288 278 ->setIcon('fa-ban')); 289 279 } 290 280 291 - $can_run = ($has_manage && $plan->canRunManually()); 281 + $can_run = ($can_edit && $plan->canRunManually()); 292 282 293 283 $list->addAction( 294 284 id(new PhabricatorActionView())
-3
src/applications/harbormaster/controller/HarbormasterStepAddController.php
··· 5 5 public function handleRequest(AphrontRequest $request) { 6 6 $viewer = $this->getViewer(); 7 7 8 - $this->requireApplicationCapability( 9 - HarbormasterManagePlansCapability::CAPABILITY); 10 - 11 8 $plan = id(new HarbormasterBuildPlanQuery()) 12 9 ->setViewer($viewer) 13 10 ->withIDs(array($request->getURIData('id')))
-3
src/applications/harbormaster/controller/HarbormasterStepDeleteController.php
··· 5 5 public function handleRequest(AphrontRequest $request) { 6 6 $viewer = $this->getViewer(); 7 7 8 - $this->requireApplicationCapability( 9 - HarbormasterManagePlansCapability::CAPABILITY); 10 - 11 8 $id = $request->getURIData('id'); 12 9 13 10 $step = id(new HarbormasterBuildStepQuery())
-3
src/applications/harbormaster/controller/HarbormasterStepEditController.php
··· 6 6 $viewer = $this->getViewer(); 7 7 $id = $request->getURIData('id'); 8 8 9 - $this->requireApplicationCapability( 10 - HarbormasterManagePlansCapability::CAPABILITY); 11 - 12 9 if ($id) { 13 10 $step = id(new HarbormasterBuildStepQuery()) 14 11 ->setViewer($viewer)
+2 -1
src/applications/harbormaster/editor/HarbormasterBuildPlanEditor.php
··· 15 15 $types = parent::getTransactionTypes(); 16 16 $types[] = HarbormasterBuildPlanTransaction::TYPE_NAME; 17 17 $types[] = HarbormasterBuildPlanTransaction::TYPE_STATUS; 18 - $types[] = PhabricatorTransactions::TYPE_COMMENT; 18 + $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 19 + $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; 19 20 return $types; 20 21 } 21 22
+20 -7
src/applications/harbormaster/storage/configuration/HarbormasterBuildPlan.php
··· 12 12 protected $name; 13 13 protected $planStatus; 14 14 protected $planAutoKey; 15 + protected $viewPolicy; 16 + protected $editPolicy; 15 17 16 18 const STATUS_ACTIVE = 'active'; 17 19 const STATUS_DISABLED = 'disabled'; ··· 19 21 private $buildSteps = self::ATTACHABLE; 20 22 21 23 public static function initializeNewBuildPlan(PhabricatorUser $actor) { 24 + $app = id(new PhabricatorApplicationQuery()) 25 + ->setViewer($actor) 26 + ->withClasses(array('PhabricatorHarbormasterApplication')) 27 + ->executeOne(); 28 + 29 + $view_policy = $app->getPolicy( 30 + HarbormasterBuildPlanDefaultViewCapability::CAPABILITY); 31 + $edit_policy = $app->getPolicy( 32 + HarbormasterBuildPlanDefaultEditCapability::CAPABILITY); 33 + 22 34 return id(new HarbormasterBuildPlan()) 23 35 ->setName('') 24 36 ->setPlanStatus(self::STATUS_ACTIVE) 25 - ->attachBuildSteps(array()); 37 + ->attachBuildSteps(array()) 38 + ->setViewPolicy($view_policy) 39 + ->setEditPolicy($edit_policy); 26 40 } 27 41 28 42 protected function getConfiguration() { ··· 156 170 public function getPolicy($capability) { 157 171 switch ($capability) { 158 172 case PhabricatorPolicyCapability::CAN_VIEW: 159 - return PhabricatorPolicies::getMostOpenPolicy(); 173 + if ($this->isAutoplan()) { 174 + return PhabricatorPolicies::getMostOpenPolicy(); 175 + } 176 + return $this->getViewPolicy(); 160 177 case PhabricatorPolicyCapability::CAN_EDIT: 161 - // NOTE: In practice, this policy is always limited by the "Mangage 162 - // Build Plans" policy. 163 - 164 178 if ($this->isAutoplan()) { 165 179 return PhabricatorPolicies::POLICY_NOONE; 166 180 } 167 - 168 - return PhabricatorPolicies::getMostOpenPolicy(); 181 + return $this->getEditPolicy(); 169 182 } 170 183 } 171 184