diff --git a/src/Forms/DefaultFormFactory.php b/src/Forms/DefaultFormFactory.php index 8b737d0481e..04d2e89fe7b 100644 --- a/src/Forms/DefaultFormFactory.php +++ b/src/Forms/DefaultFormFactory.php @@ -47,6 +47,15 @@ public function getForm(RequestHandler $controller = null, $name = FormFactory:: $validator = $this->getFormValidator($controller, $name, $context); $form = Form::create($controller, $name, $fields, $actions, $validator); + // Sudo mode + if (is_a($context['Record'], DataObject::class)) { + /** @var DataObject $record */ + $record = $context['Record']; + if ($record->getRequireSudoMode()) { + $form->requireSudoMode(); + } + } + // Extend form $this->invokeWithExtensions('updateForm', $form, $controller, $name, $context); diff --git a/src/Forms/Form.php b/src/Forms/Form.php index a1a8af6e3b5..a3cfa599f87 100644 --- a/src/Forms/Form.php +++ b/src/Forms/Form.php @@ -3,6 +3,7 @@ namespace SilverStripe\Forms; use BadMethodCallException; +use SilverStripe\Admin\LeftAndMain; use SilverStripe\Control\Controller; use SilverStripe\Control\HasRequestHandler; use SilverStripe\Control\HTTPRequest; @@ -21,6 +22,8 @@ use SilverStripe\View\SSViewer; use SilverStripe\View\ViewableData; use SilverStripe\Dev\Deprecation; +use SilverStripe\Security\SudoMode\SudoModeServiceInterface; +use SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest; /** * Base class for all forms. @@ -317,6 +320,38 @@ public function __construct( $this->setupDefaultClasses(); } + public function requireSudoMode(): void + { + // sudo mode only makes sense in a CMS context i.e. not on frontend forms + $classes = [ + LeftAndMain::class, + GridFieldDetailForm_ItemRequest::class, + ]; + $matched = false; + foreach ($classes as $class) { + if (is_a($this->getController(), $class)) { + $matched = true; + break; + } + } + if (!$matched) { + return; + } + $service = Injector::inst()->get(SudoModeServiceInterface::class); + $session = $this->getRequest()->getSession(); + if ($service->check($session)) { + return; + } + $this->makeReadonly(); + $name = SudoModePasswordField::FIELD_NAME; + if ($this->Fields()->fieldByName($name)) { + $this->Fields()->removeByName($name); + } + $field = new SudoModePasswordField($name); + $field->setForm($this); + $this->Fields()->insertBefore($this->Fields()->first()->getName(), $field); + } + /** * @return bool */ diff --git a/src/Forms/GridField/GridField.php b/src/Forms/GridField/GridField.php index d6039fe4c93..09c8940247c 100644 --- a/src/Forms/GridField/GridField.php +++ b/src/Forms/GridField/GridField.php @@ -28,6 +28,7 @@ use SilverStripe\ORM\SS_List; use SilverStripe\View\HTML; use SilverStripe\View\ViewableData; +use SilverStripe\Security\SudoMode\SudoModeServiceInterface; /** * Displays a {@link SS_List} in a grid format. @@ -630,6 +631,21 @@ public function FieldHolder($properties = []) } } + // Set to read-only if sudo mode is required for the DataObject being managed + if (is_a($list, DataList::class)) { + /** @var DataObject $obj */ + $obj = Injector::inst()->create($list->dataClass); + if ($obj->getRequireSudoMode()) { + $session = Controller::curr()?->getRequest()?->getSession(); + if ($session) { + $service = Injector::inst()->get(SudoModeServiceInterface::class); + if (!$service->check($session)) { + $this->performReadonlyTransformation(); + } + } + } + } + $total = count($list ?? []); if ($total > 0) { diff --git a/src/Forms/GridField/GridFieldDetailForm_ItemRequest.php b/src/Forms/GridField/GridFieldDetailForm_ItemRequest.php index 5421edc8b1a..bb9c722b8ed 100644 --- a/src/Forms/GridField/GridFieldDetailForm_ItemRequest.php +++ b/src/Forms/GridField/GridFieldDetailForm_ItemRequest.php @@ -322,6 +322,11 @@ public function ItemEditForm() if ($cb) { $cb($form, $this); } + + if (is_a($this->record, DataObject::class) && $this->record->getRequireSudoMode()) { + $form->requireSudoMode(); + } + $this->extend("updateItemEditForm", $form); return $form; } diff --git a/src/Forms/SudoModePasswordField.php b/src/Forms/SudoModePasswordField.php new file mode 100644 index 00000000000..0bd36e3c933 --- /dev/null +++ b/src/Forms/SudoModePasswordField.php @@ -0,0 +1,23 @@ +config()->get('requireSudoMode'); + } + /** * Human-readable singular name. * @var string diff --git a/src/Security/Member.php b/src/Security/Member.php index 6542c57933a..7bf8b0b7d83 100644 --- a/src/Security/Member.php +++ b/src/Security/Member.php @@ -67,6 +67,8 @@ */ class Member extends DataObject { + private static bool $requireSudoMode = true; // todo Group, Role, etc + private static $db = [ 'FirstName' => 'Varchar', 'Surname' => 'Varchar', diff --git a/src/Security/MemberAuthenticator/SessionAuthenticationHandler.php b/src/Security/MemberAuthenticator/SessionAuthenticationHandler.php index 252bb749315..d18658adf23 100644 --- a/src/Security/MemberAuthenticator/SessionAuthenticationHandler.php +++ b/src/Security/MemberAuthenticator/SessionAuthenticationHandler.php @@ -82,11 +82,6 @@ public function logIn(Member $member, $persistent = false, HTTPRequest $request if (Member::config()->get('login_marker_cookie')) { Cookie::set(Member::config()->get('login_marker_cookie'), 1, 0); } - - // Activate sudo mode on login so the user doesn't have to reauthenticate for sudo - // actions until the sudo mode timeout expires - $service = Injector::inst()->get(SudoModeServiceInterface::class); - $service->activate($session); } /** diff --git a/src/Security/Member_Validator.php b/src/Security/Member_Validator.php index af1f5448e6a..44f17a99c3c 100644 --- a/src/Security/Member_Validator.php +++ b/src/Security/Member_Validator.php @@ -144,7 +144,7 @@ public function php($data) ) { $stillAdmin = true; - if (!isset($data['DirectGroups'])) { + if (!isset($data['DirectGroups']) || !is_array($data['DirectGroups'])) { $stillAdmin = false; } else { $adminGroups = array_intersect(