diff --git a/core/core.services.yml b/core/core.services.yml index 60fd6d5..677d21c 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -289,7 +289,7 @@ services: arguments: ['@stream_wrapper_manager', '@settings', '@logger.channel.file'] form_builder: class: Drupal\Core\Form\FormBuilder - arguments: ['@form_validator', '@form_submitter', '@form_cache', '@module_handler', '@event_dispatcher', '@request_stack', '@class_resolver', '@element_info', '@theme.manager', '@?csrf_token'] + arguments: ['@form_validator', '@form_submitter', '@form_cache', '@module_handler', '@event_dispatcher', '@request_stack', '@class_resolver', '@element_info', '@theme.manager', '@private_key', '@keyvalue.expirable', '@?csrf_token'] form_validator: class: Drupal\Core\Form\FormValidator arguments: ['@request_stack', '@string_translation', '@csrf_token', '@logger.channel.form'] diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php index fa411a2..af408a2 100644 --- a/core/lib/Drupal/Core/Form/FormBuilder.php +++ b/core/lib/Drupal/Core/Form/FormBuilder.php @@ -15,6 +15,8 @@ use Drupal\Core\Access\CsrfTokenGenerator; use Drupal\Core\DependencyInjection\ClassResolverInterface; use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface; +use Drupal\Core\PrivateKey; use Drupal\Core\Render\Element; use Drupal\Core\Render\ElementInfoManagerInterface; use Drupal\Core\Site\Settings; @@ -104,6 +106,20 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS protected $formCache; /** + * The private key service. + * + * @var \Drupal\Core\PrivateKey + */ + protected $privateKey; + + /** + * The expirable key value factory. + * + * @var \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface + */ + protected $keyValueExpirableFactory; + + /** * Constructs a new FormBuilder. * * @param \Drupal\Core\Form\FormValidatorInterface $form_validator @@ -124,10 +140,12 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS * The element info manager. * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager * The theme manager. + * @param \Drupal\Core\PrivateKey $private_key + * @param \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface $key_value_expirable_factory * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token * The CSRF token generator. */ - public function __construct(FormValidatorInterface $form_validator, FormSubmitterInterface $form_submitter, FormCacheInterface $form_cache, ModuleHandlerInterface $module_handler, EventDispatcherInterface $event_dispatcher, RequestStack $request_stack, ClassResolverInterface $class_resolver, ElementInfoManagerInterface $element_info, ThemeManagerInterface $theme_manager, CsrfTokenGenerator $csrf_token = NULL) { + public function __construct(FormValidatorInterface $form_validator, FormSubmitterInterface $form_submitter, FormCacheInterface $form_cache, ModuleHandlerInterface $module_handler, EventDispatcherInterface $event_dispatcher, RequestStack $request_stack, ClassResolverInterface $class_resolver, ElementInfoManagerInterface $element_info, ThemeManagerInterface $theme_manager, PrivateKey $private_key, KeyValueExpirableFactoryInterface $key_value_expirable_factory, CsrfTokenGenerator $csrf_token = NULL) { $this->formValidator = $form_validator; $this->formSubmitter = $form_submitter; $this->formCache = $form_cache; @@ -136,6 +154,8 @@ public function __construct(FormValidatorInterface $form_validator, FormSubmitte $this->requestStack = $request_stack; $this->classResolver = $class_resolver; $this->elementInfo = $element_info; + $this->privateKey = $private_key; + $this->keyValueExpirableFactory = $key_value_expirable_factory; $this->csrfToken = $csrf_token; $this->themeManager = $theme_manager; } @@ -181,10 +201,8 @@ public function getForm($form_arg) { */ public function buildForm($form_id, FormStateInterface &$form_state) { // Ensure the form ID is prepared. - $private_key = \Drupal::service('private_key'); - $keyvalue = \Drupal::service('keyvalue.expirable'); - $form_build_key = 'form_build_key:' . Crypt::hmacBase64(serialize($form_id) . serialize($form_state), 'form_key' . $private_key->get() . Settings::getHashSalt()); - $keyvalue->get('form_build_key')->setWithExpire($form_build_key, [$form_id, $form_state], 21600); + $form_build_key = 'form_build_key:' . Crypt::hmacBase64(serialize($form_id) . serialize($form_state), 'form_key' . $this->privateKey->get() . Settings::getHashSalt()); + $this->keyValueExpirableFactory->get('form_build_key')->setWithExpire($form_build_key, [$form_id, $form_state], 21600); $form_id = $this->getFormId($form_id, $form_state); $input = $form_state->getUserInput(); diff --git a/core/tests/Drupal/Tests/Core/Form/FormTestBase.php b/core/tests/Drupal/Tests/Core/Form/FormTestBase.php index 12b3260..8ae2abb 100644 --- a/core/tests/Drupal/Tests/Core/Form/FormTestBase.php +++ b/core/tests/Drupal/Tests/Core/Form/FormTestBase.php @@ -13,6 +13,7 @@ use Drupal\Core\Form\FormState; use Drupal\Core\Form\FormStateInterface; use Drupal\Core\Session\AccountInterface; +use Drupal\Core\Site\Settings; use Drupal\Tests\UnitTestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; @@ -133,11 +134,6 @@ protected $translationManager; /** - * @var \Drupal\Core\DrupalKernelInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $kernel; - - /** * @var \PHPUnit_Framework_MockObject_MockObject|\Psr\Log\LoggerInterface */ protected $logger; @@ -149,6 +145,16 @@ */ protected $themeManager; + /** + * The expirable key value factory. + * + * @var \Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $keyValueExpirableFactory; + + /** + * {@inheritdoc} + */ protected function setUp() { $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); @@ -172,6 +178,21 @@ protected function setUp() { ->disableOriginalConstructor() ->getMock(); $this->account = $this->getMock('Drupal\Core\Session\AccountInterface'); + + $form_build_key_store = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface'); + $this->keyValueExpirableFactory = $this->getMock('Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface'); + $this->keyValueExpirableFactory->expects($this->any()) + ->method('get') + ->with('form_build_key') + ->willReturn($form_build_key_store); + // Set up the settings singleton. + new Settings([ + 'hash_salt' => $this->randomMachineName(), + ]); + $private_key = $this->getMockBuilder('Drupal\Core\PrivateKey') + ->disableOriginalConstructor() + ->getMock(); + $this->themeManager = $this->getMock('Drupal\Core\Theme\ThemeManagerInterface'); $this->request = new Request(); $this->eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface'); @@ -188,7 +209,7 @@ protected function setUp() { ->getMock(); $this->root = dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__)))); - $this->formBuilder = new FormBuilder($this->formValidator, $this->formSubmitter, $this->formCache, $this->moduleHandler, $this->eventDispatcher, $this->requestStack, $this->classResolver, $this->elementInfo, $this->themeManager, $this->csrfToken, $this->kernel); + $this->formBuilder = new FormBuilder($this->formValidator, $this->formSubmitter, $this->formCache, $this->moduleHandler, $this->eventDispatcher, $this->requestStack, $this->classResolver, $this->elementInfo, $this->themeManager, $private_key, $this->keyValueExpirableFactory, $this->csrfToken); } /**