From 8cfc37f8fcbabae49c3c9da0fe35247eac99d893 Mon Sep 17 00:00:00 2001 From: Bob Trahan Date: Tue, 13 Jan 2015 11:46:29 -0800 Subject: [PATCH] Config - add an option to lock policy settings Summary: Fixes T6947 Test Plan: locked people.create.user and noted the UI only showed a link to the existing policy with no way to edit it. tried to set the config to all the various bad things and saw helpful error messages telling me what I did wrong. Reviewers: epriestley Reviewed By: epriestley Subscribers: Korvin, epriestley Maniphest Tasks: T6947 Differential Revision: https://secure.phabricator.com/D11358 --- src/__phutil_library_map__.php | 2 + .../PhabricatorApplicationEditController.php | 20 +++--- .../config/PhabricatorPolicyConfigOptions.php | 16 +++++ .../policy/config/PolicyLockOptionType.php | 63 +++++++++++++++++++ 4 files changed, 90 insertions(+), 11 deletions(-) create mode 100644 src/applications/policy/config/PolicyLockOptionType.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 2d77574016..73381381bb 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -2864,6 +2864,7 @@ phutil_register_library_map(array( 'PhrictionTransactionComment' => 'applications/phriction/storage/PhrictionTransactionComment.php', 'PhrictionTransactionEditor' => 'applications/phriction/editor/PhrictionTransactionEditor.php', 'PhrictionTransactionQuery' => 'applications/phriction/query/PhrictionTransactionQuery.php', + 'PolicyLockOptionType' => 'applications/policy/config/PolicyLockOptionType.php', 'PonderAddAnswerView' => 'applications/ponder/view/PonderAddAnswerView.php', 'PonderAnswer' => 'applications/ponder/storage/PonderAnswer.php', 'PonderAnswerCommentController' => 'applications/ponder/controller/PonderAnswerCommentController.php', @@ -6194,6 +6195,7 @@ phutil_register_library_map(array( 'PhrictionTransactionComment' => 'PhabricatorApplicationTransactionComment', 'PhrictionTransactionEditor' => 'PhabricatorApplicationTransactionEditor', 'PhrictionTransactionQuery' => 'PhabricatorApplicationTransactionQuery', + 'PolicyLockOptionType' => 'PhabricatorConfigJSONOptionType', 'PonderAddAnswerView' => 'AphrontView', 'PonderAnswer' => array( 'PonderDAO', diff --git a/src/applications/meta/controller/PhabricatorApplicationEditController.php b/src/applications/meta/controller/PhabricatorApplicationEditController.php index c56d0a83e9..fa630d71ec 100644 --- a/src/applications/meta/controller/PhabricatorApplicationEditController.php +++ b/src/applications/meta/controller/PhabricatorApplicationEditController.php @@ -3,23 +3,17 @@ final class PhabricatorApplicationEditController extends PhabricatorApplicationsController { - private $application; - public function shouldRequireAdmin() { return true; } - public function willProcessRequest(array $data) { - $this->application = $data['application']; - } - - public function processRequest() { - $request = $this->getRequest(); + public function handleRequest(AphrontRequest $request) { $user = $request->getUser(); + $application = $request->getURIData('application'); $application = id(new PhabricatorApplicationQuery()) ->setViewer($user) - ->withClasses(array($this->application)) + ->withClasses(array($application)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, @@ -107,7 +101,7 @@ final class PhabricatorApplicationEditController $user, $config_entry, $value, - PhabricatorContentSource::newFromRequest($this->getRequest())); + PhabricatorContentSource::newFromRequest($request)); } return id(new AphrontRedirectResponse())->setURI($view_uri); @@ -120,12 +114,15 @@ final class PhabricatorApplicationEditController $form = id(new AphrontFormView()) ->setUser($user); + $locked_policies = PhabricatorEnv::getEnvConfig('policy.locked'); + $locked_map = array_fill_keys($locked_policies, true); foreach ($application->getCapabilities() as $capability) { $label = $application->getCapabilityLabel($capability); $can_edit = $application->isCapabilityEditable($capability); + $locked = idx($locked_map, $capability); $caption = $application->getCapabilityCaption($capability); - if (!$can_edit) { + if (!$can_edit || $locked) { $form->appendChild( id(new AphrontFormStaticControl()) ->setLabel($label) @@ -135,6 +132,7 @@ final class PhabricatorApplicationEditController $form->appendChild( id(new AphrontFormPolicyControl()) ->setUser($user) + ->setDisabled(idx($locked_map, $capability)) ->setCapability($capability) ->setPolicyObject($application) ->setPolicies($policies) diff --git a/src/applications/policy/config/PhabricatorPolicyConfigOptions.php b/src/applications/policy/config/PhabricatorPolicyConfigOptions.php index 5637f9dcdf..1f34822a32 100644 --- a/src/applications/policy/config/PhabricatorPolicyConfigOptions.php +++ b/src/applications/policy/config/PhabricatorPolicyConfigOptions.php @@ -12,6 +12,12 @@ final class PhabricatorPolicyConfigOptions } public function getOptions() { + $policy_locked_type = 'custom:PolicyLockOptionType'; + $policy_locked_example = array( + 'people.create.users' => 'admin',); + $json = new PhutilJSON(); + $policy_locked_example = $json->encodeFormatted($policy_locked_example); + return array( $this->newOption('policy.allow-public', 'bool', false) ->setBoolOptions( @@ -39,6 +45,16 @@ final class PhabricatorPolicyConfigOptions "With this setting disabled, the 'Public' policy is not ". "available, and the most open policy is 'All Users' (which means ". "users must have accounts and be logged in to view things).")), + $this->newOption('policy.locked', $policy_locked_type, array()) + ->setSummary(pht( + 'Lock specific application policies so they can not be edited.')) + ->setDescription(pht( + 'Phabricator has application policies which can dictate whether '. + 'users can take certain actions, such as creating new users. '."\n\n". + 'This setting allows for "locking" these policies such that no '. + 'further edits can be made on a per-policy basis.')) + ->addExample($policy_locked_example, + pht('Lock Create User Policy To Admins')), ); } diff --git a/src/applications/policy/config/PolicyLockOptionType.php b/src/applications/policy/config/PolicyLockOptionType.php new file mode 100644 index 0000000000..3198a927c2 --- /dev/null +++ b/src/applications/policy/config/PolicyLockOptionType.php @@ -0,0 +1,63 @@ +setAncestorClass('PhabricatorPolicyCapability') + ->loadObjects(); + $capabilities = mpull($capabilities, null, 'getCapabilityKey'); + + $policy_phids = array(); + foreach ($value as $capability_key => $policy) { + $capability = idx($capabilities, $capability_key); + if (!$capability) { + throw new Exception(pht( + 'Capability "%s" does not exist.', $capability_key)); + } + if (phid_get_type($policy) != + PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN) { + $policy_phids[$policy] = $policy; + } else { + try { + $policy_object = PhabricatorPolicyQuery::getGlobalPolicy($policy); + // this exception is not helpful here as its about global policy; + // throw a better exception + } catch (Exception $ex) { + throw new Exception(pht( + 'Capability "%s" has invalid policy "%s".', + $capability_key, + $policy)); + } + } + + if ($policy == PhabricatorPolicies::POLICY_PUBLIC) { + if (!$capability->shouldAllowPublicPolicySetting()) { + throw new Exception(pht( + 'Capability "%s" does not support public policy.', + $capability_key)); + } + } + } + + if ($policy_phids) { + $handles = id(new PhabricatorHandleQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withPhids($policy_phids) + ->execute(); + $handles = mpull($handles, null, 'getPHID'); + foreach ($value as $capability_key => $policy) { + $handle = $handles[$policy]; + if (!$handle->isComplete()) { + throw new Exception(pht( + 'Capability "%s" has invalid policy "%s"; "%s" does not exist.', + $capability_key, + $policy, + $policy)); + } + } + } + } + +}