Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add buildtask and dev URL permissions #10979

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 1 addition & 6 deletions _config/requestprocessors.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ SilverStripe\Core\Injector\Injector:
DevUrlsConfirmationMiddleware: '%$DevUrlsConfirmationMiddleware'

DevUrlsConfirmationMiddleware:
class: SilverStripe\Control\Middleware\PermissionAwareConfirmationMiddleware
class: SilverStripe\Control\Middleware\DevelopmentAdminConfirmationMiddleware
constructor:
- '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\UrlPathStartswith("dev")'
properties:
Expand All @@ -97,8 +97,6 @@ SilverStripe\Core\Injector\Injector:
- '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\CliBypass'
- '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\EnvironmentBypass("dev")'
EnforceAuthentication: false
AffectedPermissions:
- ADMIN

---
Name: dev_urls-confirmation-exceptions
Expand All @@ -123,9 +121,6 @@ SilverStripe\Core\Injector\Injector:
DevUrlsConfirmationMiddleware:
properties:
Bypasses:
# dev/build is covered by URLSpecialsMiddleware
- '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\UrlPathStartswith("dev/build")'

# The confirmation form is where people will be redirected for confirmation. We don't want to block it.
- '%$SilverStripe\Control\Middleware\ConfirmationMiddleware\UrlPathStartswith("dev/confirm")'

Expand Down
63 changes: 63 additions & 0 deletions src/Control/Middleware/DevelopmentAdminConfirmationMiddleware.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<?php

namespace SilverStripe\Control\Middleware;

use SilverStripe\Control\HTTPRequest;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\DevelopmentAdmin;
use SilverStripe\Security\Permission;

/**
* Extends the PermissionAwareConfirmationMiddleware with checks for user permissions
*
* Respects users who don't have enough access and does not
* ask them for confirmation
*
* By default it enforces authentication by redirecting users to a login page.
*
* How it works:
* - if user can bypass the middleware, then pass request further
* - if there are no confirmation items, then pass request further
* - if user is not authenticated and enforceAuthentication is false, then pass request further
* - if user does not have at least one of the affected permissions, then pass request further
* - otherwise, pass handling to the parent (ConfirmationMiddleware)
*/
class DevelopmentAdminConfirmationMiddleware extends PermissionAwareConfirmationMiddleware
{

/**
* Check whether the user has permissions to perform the target operation
* Otherwise we may want to skip the confirmation dialog.
*
* WARNING! The user has to be authenticated beforehand
*
* @param HTTPRequest $request
*
* @return bool
*/
public function hasAccess(HTTPRequest $request)
{
$action = $request->remaining();
if (empty($action)) {
return false;
}

$registeredRoutes = DevelopmentAdmin::config()->get('registered_controllers');
while (!isset($registeredRoutes[$action]) && strpos($action, '/') !== false) {
// Check for the parent route if a specific route isn't found
$action = substr($action, 0, strrpos($action, '/'));
}

if (isset($registeredRoutes[$action]['controller'])) {
$initPermissions = Config::forClass($registeredRoutes[$action]['controller'])->get('init_permissions');
foreach ($initPermissions as $permission) {
if (Permission::check($permission)) {
return true;
}
}
}

return false;
}
}
3 changes: 1 addition & 2 deletions src/Control/Middleware/URLSpecialsMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,7 @@ public function __construct()
parent::__construct(
new ConfirmationMiddleware\GetParameter("flush"),
new ConfirmationMiddleware\GetParameter("isDev"),
new ConfirmationMiddleware\GetParameter("isTest"),
new ConfirmationMiddleware\UrlPathStartswith("dev/build")
new ConfirmationMiddleware\GetParameter("isTest")
);
}

Expand Down
43 changes: 42 additions & 1 deletion src/Dev/DevBuildController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@
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 = [
Expand All @@ -19,6 +22,21 @@ class DevBuildController extends Controller
'build'
];

private static $init_permissions = [
'ADMIN',
'ALL_DEV_ADMIN',
'CAN_DEV_BUILD',
];

protected function init(): void
{
parent::init();

if (!$this->canInit()) {
Security::permissionFailure($this);
}
}

public function build(HTTPRequest $request): HTTPResponse
{
if (Director::is_cli()) {
Expand All @@ -39,4 +57,27 @@ public function build(HTTPRequest $request): HTTPResponse
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(static::config()->get('init_permissions'))
);
}

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
],
];
}
}
45 changes: 43 additions & 2 deletions src/Dev/DevConfigController.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,16 @@
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
{

/**
Expand All @@ -32,6 +35,21 @@ class DevConfigController extends Controller
'audit',
];

private static $init_permissions = [
'ADMIN',
'ALL_DEV_ADMIN',
'CAN_DEV_CONFIG',
];

protected function init(): void
{
parent::init();

if (!$this->canInit()) {
Security::permissionFailure($this);
}
}

/**
* Note: config() method is already defined, so let's just use index()
*
Expand Down Expand Up @@ -129,6 +147,29 @@ public function audit()
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(static::config()->get('init_permissions'))
);
}

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
*
Expand Down
Loading
Loading