Issue #3274867 by longwave, jungle, andypost, alexpott, smustgrave: Add TrustedCallback attribute

merge-requests/3620/merge
Alex Pott 2023-04-17 10:43:35 +01:00
parent f06e188e2f
commit 2420d38b57
No known key found for this signature in database
GPG Key ID: BDA67E7EE836E5CE
3 changed files with 30 additions and 3 deletions

View File

@ -0,0 +1,9 @@
<?php
namespace Drupal\Core\Security\Attribute;
/**
* Attribute to tell that a method is a trusted callback.
*/
#[\Attribute(\Attribute::TARGET_METHOD)]
class TrustedCallback {}

View File

@ -2,9 +2,12 @@
namespace Drupal\Core\Security;
use Drupal\Core\Security\Attribute\TrustedCallback;
/**
* Ensures that TrustedCallbackInterface can be enforced for callback methods.
* Ensures that only predefined methods can be used as callback methods.
*
* @see \Drupal\Core\Security\Attribute\TrustedCallback
* @see \Drupal\Core\Security\TrustedCallbackInterface
*/
trait DoTrustedCallbackTrait {
@ -13,8 +16,10 @@ trait DoTrustedCallbackTrait {
* Performs a callback.
*
* If the callback is trusted the callback will occur. Trusted callbacks must
* be methods of a class that implements
* \Drupal\Core\Security\TrustedCallbackInterface or $extra_trusted_interface
* be methods that are tagged with the
* \Drupal\Core\Security\Attribute\TrustedCallback attribute, or be methods of
* a class that implements
* \Drupal\Core\Security\TrustedCallbackInterface or $extra_trusted_interface,
* or be an anonymous function. If the callback is not trusted then whether or
* not the callback is called and what type of error is thrown depends on
* $error_type. To provide time for dependent code to use trusted callbacks
@ -46,6 +51,7 @@ trait DoTrustedCallbackTrait {
* Exception thrown if the callback is not trusted and $error_type equals
* TrustedCallbackInterface::THROW_EXCEPTION.
*
* @see \Drupal\Core\Security\Attribute\TrustedCallback
* @see \Drupal\Core\Security\TrustedCallbackInterface
*/
public function doTrustedCallback(callable $callback, array $args, $message, $error_type = TrustedCallbackInterface::THROW_EXCEPTION, $extra_trusted_interface = NULL) {
@ -72,6 +78,10 @@ trait DoTrustedCallbackTrait {
}
$safe_callback = in_array($method_name, $methods, TRUE);
}
if (!$safe_callback) {
$method = new \ReflectionMethod($object_or_classname, $method_name);
$safe_callback = (bool) $method->getAttributes(TrustedCallback::class);
}
}
elseif ($callback instanceof \Closure) {
$safe_callback = TRUE;

View File

@ -2,6 +2,7 @@
namespace Drupal\Tests\Core\Security;
use Drupal\Core\Security\Attribute\TrustedCallback;
use Drupal\Core\Security\TrustedCallbackInterface;
use Drupal\Core\Security\DoTrustedCallbackTrait;
use Drupal\Core\Security\UntrustedCallbackException;
@ -33,8 +34,10 @@ class DoTrustedCallbackTraitTest extends UnitTestCase {
$tests['closure'] = [$closure];
$tests['TrustedCallbackInterface_object'] = [[new TrustedMethods(), 'callback'], TrustedInterface::class];
$tests['TrustedCallbackInterface_object_attribute'] = [[new TrustedMethods(), 'attributeCallback'], TrustedInterface::class];
$tests['TrustedCallbackInterface_static_string'] = ['\Drupal\Tests\Core\Security\TrustedMethods::callback', TrustedInterface::class];
$tests['TrustedCallbackInterface_static_array'] = [[TrustedMethods::class, 'callback'], TrustedInterface::class];
$tests['TrustedCallbackInterface_static_array_attribute'] = [[TrustedMethods::class, 'attributeCallback'], TrustedInterface::class];
$tests['extra_trusted_interface_object'] = [[new TrustedObject(), 'callback'], TrustedInterface::class];
$tests['extra_trusted_interface_static_string'] = ['\Drupal\Tests\Core\Security\TrustedObject::callback', TrustedInterface::class];
$tests['extra_trusted_interface_static_array'] = [[TrustedObject::class, 'callback'], TrustedInterface::class];
@ -140,6 +143,11 @@ class TrustedMethods implements TrustedCallbackInterface {
return 'test';
}
#[TrustedCallback]
public static function attributeCallback() {
return 'test';
}
public static function unTrustedCallback() {
return 'test';
}