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)); + } + } + } + } + +}