From 3244b44a54aa6b227617bc1177449dac742ad64a Mon Sep 17 00:00:00 2001 From: Andrew Paxley Date: Wed, 12 Jul 2023 15:51:48 +1200 Subject: [PATCH] ENH add permissions for build tasks ENH add granular dev url permissions --- src/Dev/DevBuildController.php | 37 +++++- src/Dev/DevConfigController.php | 39 ++++++- src/Dev/DevelopmentAdmin.php | 107 +++++++++++++----- src/Dev/TaskRunner.php | 85 ++++++++++---- src/ORM/DatabaseAdmin.php | 34 +++--- tests/php/Dev/DevAdminControllerTest.php | 77 ++++++++++--- .../ControllerWithPermissions.php | 39 +++++++ 7 files changed, 336 insertions(+), 82 deletions(-) create mode 100644 tests/php/Dev/DevAdminControllerTest/ControllerWithPermissions.php diff --git a/src/Dev/DevBuildController.php b/src/Dev/DevBuildController.php index 5f7a949fd..a5377570e 100644 --- a/src/Dev/DevBuildController.php +++ b/src/Dev/DevBuildController.php @@ -7,8 +7,11 @@ use SilverStripe\Control\Director; use SilverStripe\Control\HTTPRequest; use SilverStripe\Control\HTTPResponse; use SilverStripe\ORM\DatabaseAdmin; +use SilverStripe\Security\Permission; +use SilverStripe\Security\PermissionProvider; +use SilverStripe\Security\Security; -class DevBuildController extends Controller +class DevBuildController extends Controller implements PermissionProvider { private static $url_handlers = [ @@ -19,6 +22,15 @@ class DevBuildController extends Controller 'build' ]; + protected function init(): void + { + parent::init(); + + if (!$this->canInit()) { + Security::permissionFailure($this); + } + } + public function build(HTTPRequest $request): HTTPResponse { if (Director::is_cli()) { @@ -39,4 +51,27 @@ class DevBuildController extends Controller return $response; } } + + public function canInit(): bool + { + return ( + Director::isDev() + // We need to ensure that DevelopmentAdminTest can simulate permission failures when running + // "dev/tasks" from CLI. + || (Director::is_cli() && DevelopmentAdmin::config()->get('allow_all_cli')) + || Permission::check(['ADMIN', 'ALL_DEV_ADMIN', 'CAN_DEV_BUILD']) + ); + } + + public function providePermissions(): array + { + return [ + 'CAN_DEV_BUILD' => [ + 'name' => _t(__CLASS__ . '.CAN_DEV_BUILD_DESCRIPTION', 'Can execute /dev/build'), + 'help' => _t(__CLASS__ . '.CAN_DEV_BUILD_HELP', 'Can execute the build command (/dev/build).'), + 'category' => DevelopmentAdmin::permissionsCategory(), + 'sort' => 100 + ], + ]; + } } diff --git a/src/Dev/DevConfigController.php b/src/Dev/DevConfigController.php index 3b5d48869..05b03f797 100644 --- a/src/Dev/DevConfigController.php +++ b/src/Dev/DevConfigController.php @@ -7,13 +7,16 @@ use SilverStripe\Control\Director; use SilverStripe\Control\HTTPResponse; use SilverStripe\Core\ClassInfo; use SilverStripe\Core\Config\Config; -use Symfony\Component\Yaml\Yaml; use SilverStripe\Core\Injector\Injector; +use SilverStripe\Security\Permission; +use SilverStripe\Security\PermissionProvider; +use SilverStripe\Security\Security; +use Symfony\Component\Yaml\Yaml; /** * Outputs the full configuration. */ -class DevConfigController extends Controller +class DevConfigController extends Controller implements PermissionProvider { /** @@ -32,6 +35,15 @@ class DevConfigController extends Controller 'audit', ]; + protected function init(): void + { + parent::init(); + + if (!$this->canInit()) { + Security::permissionFailure($this); + } + } + /** * Note: config() method is already defined, so let's just use index() * @@ -129,6 +141,29 @@ class DevConfigController extends Controller return $this->getResponse()->setBody($body); } + public function canInit(): bool + { + return ( + Director::isDev() + // We need to ensure that DevelopmentAdminTest can simulate permission failures when running + // "dev/tasks" from CLI. + || (Director::is_cli() && DevelopmentAdmin::config()->get('allow_all_cli')) + || Permission::check(['ADMIN', 'ALL_DEV_ADMIN', 'CAN_DEV_CONFIG']) + ); + } + + public function providePermissions(): array + { + return [ + 'CAN_DEV_CONFIG' => [ + 'name' => _t(__CLASS__ . '.CAN_DEV_CONFIG_DESCRIPTION', 'Can view /dev/config'), + 'help' => _t(__CLASS__ . '.CAN_DEV_CONFIG_HELP', 'Can view all application configuration (/dev/config).'), + 'category' => DevelopmentAdmin::permissionsCategory(), + 'sort' => 100 + ], + ]; + } + /** * Returns all the keys of a multi-dimensional array while maintining any nested structure * diff --git a/src/Dev/DevelopmentAdmin.php b/src/Dev/DevelopmentAdmin.php index 79b2e1c70..e3639c7d7 100644 --- a/src/Dev/DevelopmentAdmin.php +++ b/src/Dev/DevelopmentAdmin.php @@ -2,17 +2,20 @@ namespace SilverStripe\Dev; -use SilverStripe\Core\Config\Config; -use SilverStripe\Core\Injector\Injector; +use Exception; +use SilverStripe\Control\Controller; use SilverStripe\Control\Director; use SilverStripe\Control\HTTPRequest; use SilverStripe\Control\HTTPResponse; -use SilverStripe\Control\Controller; -use SilverStripe\Versioned\Versioned; +use SilverStripe\Core\ClassInfo; +use SilverStripe\Core\Config\Config; +use SilverStripe\Core\Injector\Injector; +use SilverStripe\Dev\Deprecation; use SilverStripe\ORM\DatabaseAdmin; use SilverStripe\Security\Permission; +use SilverStripe\Security\PermissionProvider; use SilverStripe\Security\Security; -use Exception; +use SilverStripe\Versioned\Versioned; /** * Base class for development tools. @@ -25,7 +28,7 @@ use Exception; * @todo cleanup errors() it's not even an allowed action, so can go * @todo cleanup index() html building */ -class DevelopmentAdmin extends Controller +class DevelopmentAdmin extends Controller implements PermissionProvider { private static $url_handlers = [ @@ -84,22 +87,8 @@ class DevelopmentAdmin extends Controller if (static::config()->get('deny_non_cli') && !Director::is_cli()) { return $this->httpError(404); } - - // Special case for dev/build: Defer permission checks to DatabaseAdmin->init() (see #4957) - $requestedDevBuild = (stripos($this->getRequest()->getURL() ?? '', 'dev/build') === 0) - && (stripos($this->getRequest()->getURL() ?? '', 'dev/build/defaults') === false); - - // We allow access to this controller regardless of live-status or ADMIN permission only - // if on CLI. Access to this controller is always allowed in "dev-mode", or of the user is ADMIN. - $allowAllCLI = static::config()->get('allow_all_cli'); - $canAccess = ( - $requestedDevBuild - || Director::isDev() - || (Director::is_cli() && $allowAllCLI) - // Its important that we don't run this check if dev/build was requested - || Permission::check("ADMIN") - ); - if (!$canAccess) { + + if (!$this->canViewAll() && empty($this->getLinks())) { Security::permissionFailure($this); return; } @@ -114,6 +103,7 @@ class DevelopmentAdmin extends Controller public function index() { + $links = $this->getLinks(); // Web mode if (!Director::is_cli()) { $renderer = DebugView::create(); @@ -123,7 +113,7 @@ class DevelopmentAdmin extends Controller echo '