/**
  * Generates a token based on $value, the user session, and the private key.
  *
  * The generated token is based on the session of the current user. Normally,
  * anonymous users do not have a session, so the generated token will be
  * different on every page request. To generate a token for users without a
  * session, manually start a session prior to calling this function.
  *
  * @param string $value
  *   (optional) An additional value to base the token on.
  *
  * @return string
  *   A 43-character URL-safe token for validation, based on the token seed,
  *   the hash salt provided by Settings::getHashSalt(), and the
  *   'drupal_private_key' configuration variable.
  *
  * @see \Drupal\Core\Site\Settings::getHashSalt()
  * @see \Drupal\Core\Session\SessionManager::start()
  */
 public function get($value = '')
 {
     if (empty($_SESSION['csrf_token_seed'])) {
         $_SESSION['csrf_token_seed'] = Crypt::randomBytesBase64();
     }
     return $this->computeToken($_SESSION['csrf_token_seed'], $value);
 }
Example #2
0
 /**
  * {@inheritdoc}
  */
 public function setUp()
 {
     parent::setUp();
     $this->key = Crypt::randomBytesBase64(55);
     $this->state = $this->getMock('Drupal\\Core\\State\\StateInterface');
     $this->privateKey = new PrivateKey($this->state);
 }
 /**
  * Tests that a new token seed is generated upon first use.
  *
  * @covers ::get
  */
 public function testGenerateSeedOnGet()
 {
     $key = Crypt::randomBytesBase64();
     $this->privateKey->expects($this->any())->method('get')->will($this->returnValue($key));
     $this->sessionMetadata->expects($this->once())->method('getCsrfTokenSeed')->will($this->returnValue(NULL));
     $this->sessionMetadata->expects($this->once())->method('setCsrfTokenSeed')->with($this->isType('string'));
     $this->assertInternalType('string', $this->generator->get());
 }
 /**
  * {@inheritdoc}
  */
 protected function setUp()
 {
     $this->syncDirectory = $this->publicFilesDirectory . '/config_' . Crypt::randomBytesBase64() . '/sync';
     $this->settings['config_directories'][CONFIG_SYNC_DIRECTORY] = (object) array('value' => $this->syncDirectory, 'required' => TRUE);
     // Other directories will be created too.
     $this->settings['config_directories']['custom'] = (object) array('value' => $this->publicFilesDirectory . '/config_custom', 'required' => TRUE);
     parent::setUp();
 }
Example #5
0
 /**
  * Generates a token based on $value, the user session, and the private key.
  *
  * The generated token is based on the session of the current user. Normally,
  * anonymous users do not have a session, so the generated token will be
  * different on every page request. To generate a token for users without a
  * session, manually start a session prior to calling this function.
  *
  * @param string $value
  *   (optional) An additional value to base the token on.
  *
  * @return string
  *   A 43-character URL-safe token for validation, based on the token seed,
  *   the hash salt provided by Settings::getHashSalt(), and the
  *   'drupal_private_key' configuration variable.
  *
  * @see \Drupal\Core\Site\Settings::getHashSalt()
  * @see \Symfony\Component\HttpFoundation\Session\SessionInterface::start()
  */
 public function get($value = '')
 {
     $seed = $this->sessionMetadata->getCsrfTokenSeed();
     if (empty($seed)) {
         $seed = Crypt::randomBytesBase64();
         $this->sessionMetadata->setCsrfTokenSeed($seed);
     }
     return $this->computeToken($seed, $value);
 }
 /**
  * {@inheritdoc}
  */
 protected function setUp()
 {
     $this->configDirectory = $this->publicFilesDirectory . '/config_' . Crypt::randomBytesBase64();
     $this->settings['config_directories'][CONFIG_SYNC_DIRECTORY] = (object) array('value' => $this->configDirectory . '/sync', 'required' => TRUE);
     // Create the files directory early so we can test the error case.
     mkdir($this->publicFilesDirectory);
     // Create a file so the directory can not be created.
     file_put_contents($this->configDirectory, 'Test');
     parent::setUp();
 }
 /**
  * {@inheritdoc}
  */
 function setUp()
 {
     parent::setUp();
     $this->key = Crypt::randomBytesBase64(55);
     $this->privateKey = $this->getMockBuilder('Drupal\\Core\\PrivateKey')->disableOriginalConstructor()->setMethods(array('get'))->getMock();
     $this->privateKey->expects($this->any())->method('get')->will($this->returnValue($this->key));
     $settings = array('hash_salt' => $this->randomName());
     new Settings($settings);
     $this->generator = new CsrfTokenGenerator($this->privateKey);
 }
 /**
  * Build Acquia Solr Search Authenticator.
  *
  * @param PreExecuteRequestEvent $event
  */
 public function preExecuteRequest($event)
 {
     $request = $event->getRequest();
     $request->addParam('request_id', uniqid(), TRUE);
     $endpoint = $this->client->getEndpoint();
     $this->uri = $endpoint->getBaseUri() . $request->getUri();
     $this->nonce = Crypt::randomBytesBase64(24);
     $string = $request->getRawData();
     if (!$string) {
         $parsed_url = parse_url($this->uri);
         $path = isset($parsed_url['path']) ? $parsed_url['path'] : '/';
         $query = isset($parsed_url['query']) ? '?' . $parsed_url['query'] : '';
         $string = $path . $query;
         // For pings only.
     }
     $cookie = $this->calculateAuthCookie($string, $this->nonce);
     $request->addHeader('Cookie: ' . $cookie);
     $request->addHeader('User-Agent: ' . 'acquia_search/' . \Drupal::config('acquia_search.settings')->get('version'));
 }
 /**
  * {@inheritdoc}
  */
 protected function setUp()
 {
     parent::setUp();
     new Settings(array('hash_salt' => 'test'));
     // Account 1: 'administrator' and 'authenticated' roles.
     $roles_1 = array('administrator', 'authenticated');
     $this->account_1 = $this->getMockBuilder('Drupal\\user\\Entity\\User')->disableOriginalConstructor()->setMethods(array('getRoles'))->getMock();
     $this->account_1->expects($this->any())->method('getRoles')->will($this->returnValue($roles_1));
     // Account 2: 'authenticated' and 'administrator' roles (different order).
     $roles_2 = array('authenticated', 'administrator');
     $this->account_2 = $this->getMockBuilder('Drupal\\user\\Entity\\User')->disableOriginalConstructor()->setMethods(array('getRoles'))->getMock();
     $this->account_2->expects($this->any())->method('getRoles')->will($this->returnValue($roles_2));
     // Updated account 1: now also 'editor' role.
     $roles_1_updated = array('editor', 'administrator', 'authenticated');
     $this->account_1_updated = $this->getMockBuilder('Drupal\\user\\Entity\\User')->disableOriginalConstructor()->setMethods(array('getRoles'))->getMock();
     $this->account_1_updated->expects($this->any())->method('getRoles')->will($this->returnValue($roles_1_updated));
     // Mocked private key + cache services.
     $random = Crypt::randomBytesBase64(55);
     $this->private_key = $this->getMockBuilder('Drupal\\Core\\PrivateKey')->disableOriginalConstructor()->setMethods(array('get'))->getMock();
     $this->private_key->expects($this->any())->method('get')->will($this->returnValue($random));
     $this->cache = $this->getMockBuilder('Drupal\\Core\\Cache\\CacheBackendInterface')->disableOriginalConstructor()->getMock();
     $this->permissionsHash = new PermissionsHash($this->private_key, $this->cache);
 }
Example #10
0
 /**
  * Sends no-JS BigPipe placeholders' replacements as embedded HTML responses.
  *
  * @param string $html
  *   HTML markup.
  * @param array $no_js_placeholders
  *   Associative array; the no-JS BigPipe placeholders. Keys are the BigPipe
  *   selectors.
  * @param \Drupal\Core\Asset\AttachedAssetsInterface $cumulative_assets
  *   The cumulative assets sent so far; to be updated while rendering no-JS
  *   BigPipe placeholders.
  */
 protected function sendNoJsPlaceholders($html, $no_js_placeholders, AttachedAssetsInterface $cumulative_assets)
 {
     $fragments = explode('<div data-big-pipe-selector-nojs="', $html);
     print array_shift($fragments);
     ob_end_flush();
     flush();
     foreach ($fragments as $fragment) {
         $t = explode('"></div>', $fragment, 2);
         $placeholder = $t[0];
         if (!isset($no_js_placeholders[$placeholder])) {
             continue;
         }
         $token = Crypt::randomBytesBase64(55);
         // Render the placeholder, but include the cumulative settings assets, so
         // we can calculate the overall settings for the entire page.
         $placeholder_plus_cumulative_settings = ['placeholder' => $no_js_placeholders[$placeholder], 'cumulative_settings_' . $token => ['#attached' => ['drupalSettings' => $cumulative_assets->getSettings()]]];
         $elements = $this->renderPlaceholder($placeholder, $placeholder_plus_cumulative_settings);
         // Create a new HtmlResponse. Ensure the CSS and (non-bottom) JS is sent
         // before the HTML they're associated with. In other words: ensure the
         // critical assets for this placeholder's markup are loaded first.
         // @see \Drupal\Core\Render\HtmlResponseSubscriber
         // @see template_preprocess_html()
         $css_placeholder = '<nojs-bigpipe-placeholder-styles-placeholder token="' . $token . '">';
         $js_placeholder = '<nojs-bigpipe-placeholder-scripts-placeholder token="' . $token . '">';
         $elements['#markup'] = Markup::create($css_placeholder . $js_placeholder . (string) $elements['#markup']);
         $elements['#attached']['html_response_attachment_placeholders']['styles'] = $css_placeholder;
         $elements['#attached']['html_response_attachment_placeholders']['scripts'] = $js_placeholder;
         $html_response = new HtmlResponse();
         $html_response->setContent($elements);
         $html_response->getCacheableMetadata()->setCacheMaxAge(0);
         // Push a fake request with the asset libraries loaded so far and dispatch
         // KernelEvents::RESPONSE event. This results in the attachments for the
         // HTML response being processed by HtmlResponseAttachmentsProcessor and
         // hence:
         // - the HTML to load the CSS can be rendered.
         // - the HTML to load the JS (at the top) can be rendered.
         $fake_request = $this->requestStack->getMasterRequest()->duplicate();
         $fake_request->request->set('ajax_page_state', ['libraries' => implode(',', $cumulative_assets->getAlreadyLoadedLibraries())] + $cumulative_assets->getSettings()['ajaxPageState']);
         $this->requestStack->push($fake_request);
         $event = new FilterResponseEvent($this->httpKernel, $fake_request, HttpKernelInterface::SUB_REQUEST, $html_response);
         $this->eventDispatcher->dispatch(KernelEvents::RESPONSE, $event);
         $html_response = $event->getResponse();
         $this->requestStack->pop();
         // Send this embedded HTML response.
         print $html_response->getContent();
         print $t[1];
         flush();
         // Another placeholder was rendered and sent, track the set of asset
         // libraries sent so far. Any new settings also need to be tracked, so
         // they can be sent in ::sendPreBody().
         // @todo What if drupalSettings already was printed in the HTML <head>? That case is not yet handled. In that case, no-JS BigPipe would cause broken (incomplete) drupalSettings… This would not matter if it were only used if JS is not enabled, but that's not the only use case. However, this
         $final_settings = $html_response->getAttachments()['drupalSettings'];
         $cumulative_assets->setAlreadyLoadedLibraries(explode(',', $final_settings['ajaxPageState']['libraries']));
         $cumulative_assets->setSettings($final_settings);
     }
 }
Example #11
0
 /**
  * {@inheritdoc}
  */
 public function submitForm(array &$form, FormStateInterface $form_state)
 {
     /** @var $user \Drupal\user\UserInterface */
     $user = $form_state->getValue('user');
     user_login_finalize($user);
     $this->logger->notice('User %name used one-time login link at time %timestamp.', array('%name' => $user->getUsername(), '%timestamp' => $form_state->getValue('timestamp')));
     drupal_set_message($this->t('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.'));
     // Let the user's password be changed without the current password check.
     $token = Crypt::randomBytesBase64(55);
     $_SESSION['pass_reset_' . $user->id()] = $token;
     $form_state->setRedirect('entity.user.edit_form', array('user' => $user->id()), array('query' => array('pass-reset-token' => $token), 'absolute' => TRUE));
 }
Example #12
0
 /**
  * {@inheritdoc}
  */
 public function generateCachePlaceholder($callback, array &$context)
 {
     if (is_string($callback) && strpos($callback, '::') === FALSE) {
         $callable = $this->controllerResolver->getControllerFromDefinition($callback);
     } else {
         $callable = $callback;
     }
     if (!is_callable($callable)) {
         throw new \InvalidArgumentException('$callable must be a callable function or of the form service_id:method.');
     }
     // Generate a unique token if one is not already provided.
     $context += ['token' => Crypt::randomBytesBase64(55)];
     return '<drupal-render-cache-placeholder callback="' . $callback . '" token="' . $context['token'] . '"></drupal-render-cache-placeholder>';
 }
Example #13
0
 /**
  * Sends no-JS BigPipe placeholders' replacements as embedded HTML responses.
  *
  * @param string $html
  *   HTML markup.
  * @param array $no_js_placeholders
  *   Associative array; the no-JS BigPipe placeholders. Keys are the BigPipe
  *   selectors.
  * @param \Drupal\Core\Asset\AttachedAssetsInterface $cumulative_assets
  *   The cumulative assets sent so far; to be updated while rendering no-JS
  *   BigPipe placeholders.
  */
 protected function sendNoJsPlaceholders($html, $no_js_placeholders, AttachedAssetsInterface $cumulative_assets)
 {
     // Split the HTML on every no-JS placeholder string.
     $prepare_for_preg_split = function ($placeholder_string) {
         return '(' . preg_quote($placeholder_string, '/') . ')';
     };
     $preg_placeholder_strings = array_map($prepare_for_preg_split, array_keys($no_js_placeholders));
     $fragments = preg_split('/' . implode('|', $preg_placeholder_strings) . '/', $html, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
     foreach ($fragments as $fragment) {
         // If the fragment isn't one of the no-JS placeholders, it is the HTML in
         // between placeholders and it must be printed & flushed immediately. The
         // rest of the logic in the loop handles the placeholders.
         if (!isset($no_js_placeholders[$fragment])) {
             print $fragment;
             flush();
             continue;
         }
         $placeholder = $fragment;
         assert('isset($no_js_placeholders[$placeholder])');
         $token = Crypt::randomBytesBase64(55);
         // Render the placeholder, but include the cumulative settings assets, so
         // we can calculate the overall settings for the entire page.
         $placeholder_plus_cumulative_settings = ['placeholder' => $no_js_placeholders[$placeholder], 'cumulative_settings_' . $token => ['#attached' => ['drupalSettings' => $cumulative_assets->getSettings()]]];
         $elements = $this->renderPlaceholder($placeholder, $placeholder_plus_cumulative_settings);
         // Create a new HtmlResponse. Ensure the CSS and (non-bottom) JS is sent
         // before the HTML they're associated with. In other words: ensure the
         // critical assets for this placeholder's markup are loaded first.
         // @see \Drupal\Core\Render\HtmlResponseSubscriber
         // @see template_preprocess_html()
         $css_placeholder = '<nojs-bigpipe-placeholder-styles-placeholder token="' . $token . '">';
         $js_placeholder = '<nojs-bigpipe-placeholder-scripts-placeholder token="' . $token . '">';
         $elements['#markup'] = Markup::create($css_placeholder . $js_placeholder . (string) $elements['#markup']);
         $elements['#attached']['html_response_attachment_placeholders']['styles'] = $css_placeholder;
         $elements['#attached']['html_response_attachment_placeholders']['scripts'] = $js_placeholder;
         $html_response = new HtmlResponse();
         $html_response->setContent($elements);
         $html_response->getCacheableMetadata()->setCacheMaxAge(0);
         // Push a fake request with the asset libraries loaded so far and dispatch
         // KernelEvents::RESPONSE event. This results in the attachments for the
         // HTML response being processed by HtmlResponseAttachmentsProcessor and
         // hence:
         // - the HTML to load the CSS can be rendered.
         // - the HTML to load the JS (at the top) can be rendered.
         $fake_request = $this->requestStack->getMasterRequest()->duplicate();
         $fake_request->request->set('ajax_page_state', ['libraries' => implode(',', $cumulative_assets->getAlreadyLoadedLibraries())]);
         $this->requestStack->push($fake_request);
         $event = new FilterResponseEvent($this->httpKernel, $fake_request, HttpKernelInterface::SUB_REQUEST, $html_response);
         $this->eventDispatcher->dispatch(KernelEvents::RESPONSE, $event);
         $html_response = $event->getResponse();
         $this->requestStack->pop();
         // Send this embedded HTML response.
         print $html_response->getContent();
         flush();
         // Another placeholder was rendered and sent, track the set of asset
         // libraries sent so far. Any new settings also need to be tracked, so
         // they can be sent in ::sendPreBody().
         // @todo What if drupalSettings already was printed in the HTML <head>? That case is not yet handled. In that case, no-JS BigPipe would cause broken (incomplete) drupalSettings… This would not matter if it were only used if JS is not enabled, but that's not the only use case. However, this
         $cumulative_assets->setAlreadyLoadedLibraries(array_merge($cumulative_assets->getAlreadyLoadedLibraries(), $html_response->getAttachments()['library']));
         $cumulative_assets->setSettings($html_response->getAttachments()['drupalSettings']);
     }
 }
 /**
  * Tests child element that uses #post_render_cache but that is rendered via a
  * template.
  */
 public function testChildElementPlaceholder()
 {
     $this->setupMemoryCache();
     // Simulate the theme system/Twig: a recursive call to Renderer::render(),
     // just like the theme system or a Twig template would have done.
     $this->themeManager->expects($this->any())->method('render')->willReturnCallback(function ($hook, $vars) {
         return $this->renderer->render($vars['foo']) . "\n";
     });
     $context = ['bar' => $this->randomContextValue(), 'token' => \Drupal\Component\Utility\Crypt::randomBytesBase64(55)];
     $callback = __NAMESPACE__ . '\\PostRenderCache::placeholder';
     $placeholder = \Drupal::service('renderer')->generateCachePlaceholder($callback, $context);
     $test_element = ['#theme' => 'some_theme_function', 'foo' => ['#post_render_cache' => [$callback => [$context]], '#markup' => $placeholder, '#prefix' => '<pre>', '#suffix' => '</pre>']];
     $expected_output = '<pre><bar>' . $context['bar'] . '</bar></pre>' . "\n";
     // #cache disabled.
     $element = $test_element;
     $output = $this->renderer->renderRoot($element);
     $this->assertSame($output, $expected_output, 'Placeholder was replaced in output');
     $expected_js_settings = ['common_test' => $context];
     $this->assertSame($element['#attached']['drupalSettings'], $expected_js_settings, '#attached is modified; JavaScript setting is added to page.');
     // GET request: #cache enabled, cache miss.
     $this->setUpRequest();
     $element = $test_element;
     $element['#cache'] = ['cid' => 'render_cache_placeholder_test_GET'];
     $element['foo']['#cache'] = ['cid' => 'render_cache_placeholder_test_child_GET'];
     // Render, which will use the common-test-render-element.html.twig template.
     $output = $this->renderer->renderRoot($element);
     $this->assertSame($output, $expected_output, 'Placeholder was replaced in output');
     $this->assertTrue(isset($element['#printed']), 'No cache hit');
     $this->assertSame($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.');
     $this->assertSame($element['#attached']['drupalSettings'], $expected_js_settings, '#attached is modified; JavaScript setting is added to page.');
     // GET request: validate cached data for child element.
     $expected_token = $context['token'];
     $cached_element = $this->memoryCache->get('render_cache_placeholder_test_child_GET')->data;
     // Parse unique token out of the cached markup.
     $dom = Html::load($cached_element['#markup']);
     $xpath = new \DOMXPath($dom);
     $nodes = $xpath->query('//*[@token]');
     $this->assertEquals(1, $nodes->length, 'The token attribute was found in the cached child element markup');
     $token = '';
     if ($nodes->length) {
         $token = $nodes->item(0)->getAttribute('token');
     }
     $this->assertSame($token, $expected_token, 'The tokens are identical for the child element');
     // Verify the token is in the cached element.
     $expected_element = ['#markup' => '<pre><drupal-render-cache-placeholder callback="' . $callback . '" token="' . $expected_token . '"></drupal-render-cache-placeholder></pre>', '#attached' => [], '#post_render_cache' => [$callback => [$context]], '#cache' => ['contexts' => [], 'tags' => [], 'max-age' => Cache::PERMANENT]];
     $this->assertSame($cached_element, $expected_element, 'The correct data is cached for the child element: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
     // GET request: validate cached data (for the parent/entire render array).
     $cached_element = $this->memoryCache->get('render_cache_placeholder_test_GET')->data;
     // Parse unique token out of the cached markup.
     $dom = Html::load($cached_element['#markup']);
     $xpath = new \DOMXPath($dom);
     $nodes = $xpath->query('//*[@token]');
     $this->assertEquals(1, $nodes->length, 'The token attribute was found in the cached parent element markup');
     $token = '';
     if ($nodes->length) {
         $token = $nodes->item(0)->getAttribute('token');
     }
     $this->assertSame($token, $expected_token, 'The tokens are identical for the parent element');
     // Verify the token is in the cached element.
     $expected_element = ['#markup' => '<pre><drupal-render-cache-placeholder callback="' . $callback . '" token="' . $expected_token . '"></drupal-render-cache-placeholder></pre>' . "\n", '#attached' => [], '#post_render_cache' => [$callback => [$context]], '#cache' => ['contexts' => [], 'tags' => [], 'max-age' => Cache::PERMANENT]];
     $this->assertSame($cached_element, $expected_element, 'The correct data is cached for the parent element: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
     // GET request: validate cached data.
     // Check the cache of the child element again after the parent has been
     // rendered.
     $cached_element = $this->memoryCache->get('render_cache_placeholder_test_child_GET')->data;
     // Verify that the child element contains the correct
     // render_cache_placeholder markup.
     $dom = Html::load($cached_element['#markup']);
     $xpath = new \DOMXPath($dom);
     $nodes = $xpath->query('//*[@token]');
     $this->assertEquals(1, $nodes->length, 'The token attribute was found in the cached child element markup');
     $token = '';
     if ($nodes->length) {
         $token = $nodes->item(0)->getAttribute('token');
     }
     $this->assertSame($token, $expected_token, 'The tokens are identical for the child element');
     // Verify the token is in the cached element.
     $expected_element = ['#markup' => '<pre><drupal-render-cache-placeholder callback="' . $callback . '" token="' . $expected_token . '"></drupal-render-cache-placeholder></pre>', '#attached' => [], '#post_render_cache' => [$callback => [$context]], '#cache' => ['contexts' => [], 'tags' => [], 'max-age' => Cache::PERMANENT]];
     $this->assertSame($cached_element, $expected_element, 'The correct data is cached for the child element: the stored #markup and #attached properties are not affected by #post_render_cache callbacks.');
     // GET request: #cache enabled, cache hit.
     $element = $test_element;
     $element['#cache'] = ['cid' => 'render_cache_placeholder_test_GET'];
     // Render, which will use the common-test-render-element.html.twig template.
     $output = $this->renderer->renderRoot($element);
     $this->assertSame($output, $expected_output, 'Placeholder was replaced in output');
     $this->assertFalse(isset($element['#printed']), 'Cache hit');
     $this->assertSame($element['#markup'], $expected_output, 'Placeholder was replaced in #markup.');
     $this->assertSame($element['#attached']['drupalSettings'], $expected_js_settings, '#attached is modified; JavaScript setting is added to page.');
 }
Example #15
0
 /**
  * {@inheritdoc}
  */
 public function regenerate($destroy = FALSE, $lifetime = NULL)
 {
     // Nothing to do if we are not allowed to change the session.
     if ($this->isCli()) {
         return;
     }
     // We do not support the optional $destroy and $lifetime parameters as long
     // as #2238561 remains open.
     if ($destroy || isset($lifetime)) {
         throw new \InvalidArgumentException('The optional parameters $destroy and $lifetime of SessionManager::regenerate() are not supported currently');
     }
     if ($this->isStarted()) {
         $old_session_id = $this->getId();
     }
     session_id(Crypt::randomBytesBase64());
     $this->getMetadataBag()->clearCsrfTokenSeed();
     if (isset($old_session_id)) {
         $params = session_get_cookie_params();
         $expire = $params['lifetime'] ? REQUEST_TIME + $params['lifetime'] : 0;
         setcookie($this->getName(), $this->getId(), $expire, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
         $this->migrateStoredSession($old_session_id);
     }
     if (!$this->isStarted()) {
         // Start the session when it doesn't exist yet.
         $this->startNow();
     }
 }
Example #16
0
 /**
  * Test to be run and the results confirmed.
  *
  * Here we force test results which must match the expected results from
  * confirmStubResults().
  */
 function stubTest()
 {
     // Ensure the .htkey file exists since this is only created just before a
     // request. This allows the stub test to make requests. The event does not
     // fire here and drupal_generate_test_ua() can not generate a key for a
     // test in a test since the prefix has changed.
     // @see \Drupal\Core\Test\HttpClientMiddleware\TestHttpClientMiddleware::onBeforeSendRequest()
     // @see drupal_generate_test_ua();
     $key_file = DRUPAL_ROOT . '/sites/simpletest/' . substr($this->databasePrefix, 10) . '/.htkey';
     $private_key = Crypt::randomBytesBase64(55);
     $site_path = $this->container->get('site.path');
     file_put_contents($key_file, $private_key);
     // Check to see if runtime assertions are indeed on, if successful this
     // will be the first of sixteen passes asserted in confirmStubResults()
     try {
         // Test with minimum possible arguments to make sure no notice for
         // missing argument is thrown.
         assert(FALSE);
         $this->fail('Runtime assertions are not working.');
     } catch (\AssertionError $e) {
         try {
             // Now test with an error message to ensure it is correctly passed
             // along by the rethrow.
             assert(FALSE, 'Lorem Ipsum');
         } catch (\AssertionError $e) {
             $this->assertEqual($e->getMessage(), 'Lorem Ipsum', 'Runtime assertions Enabled and running.');
         }
     }
     // This causes the second of the sixteen passes asserted in
     // confirmStubResults().
     $this->pass($this->passMessage);
     // The first three fails are caused by enabling a non-existent module in
     // setUp().
     // This causes the fourth of the five fails asserted in
     // confirmStubResults().
     $this->fail($this->failMessage);
     // This causes the third to fifth of the sixteen passes asserted in
     // confirmStubResults().
     $user = $this->drupalCreateUser(array($this->validPermission), 'SimpleTestTest');
     // This causes the fifth of the five fails asserted in confirmStubResults().
     $this->drupalCreateUser(array($this->invalidPermission));
     // Test logging in as a user.
     // This causes the sixth to tenth of the sixteen passes asserted in
     // confirmStubResults().
     $this->drupalLogin($user);
     // This causes the eleventh of the sixteen passes asserted in
     // confirmStubResults().
     $this->pass(t('Test ID is @id.', array('@id' => $this->testId)));
     // These cause the twelfth to fifteenth of the sixteen passes asserted in
     // confirmStubResults().
     $this->assertTrue(file_exists($site_path . '/settings.testing.php'));
     // Check the settings.testing.php file got included.
     $this->assertTrue(function_exists('simpletest_test_stub_settings_function'));
     // Check that the test-specific service file got loaded.
     $this->assertTrue($this->container->has('site.service.yml'));
     $this->assertIdentical(get_class($this->container->get('cache.backend.database')), 'Drupal\\Core\\Cache\\MemoryBackendFactory');
     // These cause the two exceptions asserted in confirmStubResults().
     // Call trigger_error() without the required argument to trigger an E_WARNING.
     trigger_error();
     // Generates a warning inside a PHP function.
     array_key_exists(NULL, NULL);
     // This causes the sixteenth of the sixteen passes asserted in
     // confirmStubResults().
     $this->assertNothing();
     // This causes the debug message asserted in confirmStubResults().
     debug('Foo', 'Debug', FALSE);
 }
Example #17
0
 /**
  * {@inheritdoc}
  */
 public function regenerate($destroy = FALSE, $lifetime = NULL)
 {
     global $user;
     // Nothing to do if we are not allowed to change the session.
     if (!$this->isEnabled() || $this->isCli()) {
         return;
     }
     // We do not support the optional $destroy and $lifetime parameters as long
     // as #2238561 remains open.
     if ($destroy || isset($lifetime)) {
         throw new \InvalidArgumentException('The optional parameters $destroy and $lifetime of SessionManager::regenerate() are not supported currently');
     }
     $is_https = $this->requestStack->getCurrentRequest()->isSecure();
     $cookies = $this->requestStack->getCurrentRequest()->cookies;
     if ($is_https && $this->isMixedMode()) {
         $insecure_session_name = $this->getInsecureName();
         if ($this->isStarted() && $cookies->has($insecure_session_name)) {
             $old_insecure_session_id = $cookies->get($insecure_session_name);
         }
         $params = session_get_cookie_params();
         $session_id = Crypt::randomBytesBase64();
         // If a session cookie lifetime is set, the session will expire
         // $params['lifetime'] seconds from the current request. If it is not set,
         // it will expire when the browser is closed.
         $expire = $params['lifetime'] ? REQUEST_TIME + $params['lifetime'] : 0;
         setcookie($insecure_session_name, $session_id, $expire, $params['path'], $params['domain'], FALSE, $params['httponly']);
         $cookies->set($insecure_session_name, $session_id);
     }
     if ($this->isStarted()) {
         $old_session_id = $this->getId();
     }
     session_id(Crypt::randomBytesBase64());
     // @todo The token seed can be moved onto \Drupal\Core\Session\MetadataBag.
     //   The session manager then needs to notify the metadata bag when the
     //   token should be regenerated. https://drupal.org/node/2256257
     if (!empty($_SESSION)) {
         unset($_SESSION['csrf_token_seed']);
     }
     if (isset($old_session_id)) {
         $params = session_get_cookie_params();
         $expire = $params['lifetime'] ? REQUEST_TIME + $params['lifetime'] : 0;
         setcookie($this->getName(), $this->getId(), $expire, $params['path'], $params['domain'], $params['secure'], $params['httponly']);
         $fields = array('sid' => Crypt::hashBase64($this->getId()));
         if ($is_https) {
             $fields['ssid'] = Crypt::hashBase64($this->getId());
             // If the "secure pages" setting is enabled, use the newly-created
             // insecure session identifier as the regenerated sid.
             if ($this->isMixedMode()) {
                 $fields['sid'] = Crypt::hashBase64($session_id);
             }
         }
         $this->connection->update('sessions')->fields($fields)->condition($is_https ? 'ssid' : 'sid', Crypt::hashBase64($old_session_id))->execute();
     } elseif (isset($old_insecure_session_id)) {
         // If logging in to the secure site, and there was no active session on
         // the secure site but a session was active on the insecure site, update
         // the insecure session with the new session identifiers.
         $this->connection->update('sessions')->fields(array('sid' => Crypt::hashBase64($session_id), 'ssid' => Crypt::hashBase64($this->getId())))->condition('sid', Crypt::hashBase64($old_insecure_session_id))->execute();
     } else {
         // Start the session when it doesn't exist yet.
         // Preserve the logged in user, as it will be reset to anonymous
         // by \Drupal\Core\Session\SessionHandler::read().
         $account = $user;
         $this->start();
         $user = $account;
     }
     date_default_timezone_set(drupal_get_user_timezone());
 }
Example #18
0
 /**
  * {@inheritdoc}
  */
 public function prepareForm($form_id, &$form, &$form_state)
 {
     $user = $this->currentUser();
     $form['#type'] = 'form';
     $form_state['programmed'] = isset($form_state['programmed']) ? $form_state['programmed'] : FALSE;
     // Fix the form method, if it is 'get' in $form_state, but not in $form.
     if ($form_state['method'] == 'get' && !isset($form['#method'])) {
         $form['#method'] = 'get';
     }
     // Generate a new #build_id for this form, if none has been set already.
     // The form_build_id is used as key to cache a particular build of the form.
     // For multi-step forms, this allows the user to go back to an earlier
     // build, make changes, and re-submit.
     // @see self::buildForm()
     // @see self::rebuildForm()
     if (!isset($form['#build_id'])) {
         $form['#build_id'] = 'form-' . Crypt::randomBytesBase64();
     }
     $form['form_build_id'] = array('#type' => 'hidden', '#value' => $form['#build_id'], '#id' => $form['#build_id'], '#name' => 'form_build_id', '#parents' => array('form_build_id'));
     // Add a token, based on either #token or form_id, to any form displayed to
     // authenticated users. This ensures that any submitted form was actually
     // requested previously by the user and protects against cross site request
     // forgeries.
     // This does not apply to programmatically submitted forms. Furthermore,
     // since tokens are session-bound and forms displayed to anonymous users are
     // very likely cached, we cannot assign a token for them.
     // During installation, there is no $user yet.
     if ($user && $user->isAuthenticated() && !$form_state['programmed']) {
         // Form constructors may explicitly set #token to FALSE when cross site
         // request forgery is irrelevant to the form, such as search forms.
         if (isset($form['#token']) && $form['#token'] === FALSE) {
             unset($form['#token']);
         } else {
             $form['#token'] = $form_id;
             $form['form_token'] = array('#id' => $this->drupalHtmlId('edit-' . $form_id . '-form-token'), '#type' => 'token', '#default_value' => $this->csrfToken->get($form['#token']), '#parents' => array('form_token'));
         }
     }
     if (isset($form_id)) {
         $form['form_id'] = array('#type' => 'hidden', '#value' => $form_id, '#id' => $this->drupalHtmlId("edit-{$form_id}"), '#parents' => array('form_id'));
     }
     if (!isset($form['#id'])) {
         $form['#id'] = $this->drupalHtmlId($form_id);
     }
     $form += $this->getElementInfo('form');
     $form += array('#tree' => FALSE, '#parents' => array());
     $form['#validate'][] = array($form_state['build_info']['callback_object'], 'validateForm');
     $form['#submit'][] = array($form_state['build_info']['callback_object'], 'submitForm');
     // If no #theme has been set, automatically apply theme suggestions.
     // theme_form() itself is in #theme_wrappers and not #theme. Therefore, the
     // #theme function only has to care for rendering the inner form elements,
     // not the form itself.
     if (!isset($form['#theme'])) {
         $form['#theme'] = array($form_id);
         if (isset($form_state['build_info']['base_form_id'])) {
             $form['#theme'][] = $form_state['build_info']['base_form_id'];
         }
     }
     // Invoke hook_form_alter(), hook_form_BASE_FORM_ID_alter(), and
     // hook_form_FORM_ID_alter() implementations.
     $hooks = array('form');
     if (isset($form_state['build_info']['base_form_id'])) {
         $hooks[] = 'form_' . $form_state['build_info']['base_form_id'];
     }
     $hooks[] = 'form_' . $form_id;
     $this->moduleHandler->alter($hooks, $form, $form_state, $form_id);
 }
Example #19
0
 /**
  * Creates a new private key.
  *
  * @return string
  *   The private key.
  */
 protected function create()
 {
     return Crypt::randomBytesBase64(55);
 }
Example #20
0
 /**
  * Prepares the current environment for running the test.
  *
  * Backups various current environment variables and resets them, so they do
  * not interfere with the Drupal site installation in which tests are executed
  * and can be restored in TestBase::restoreEnvironment().
  *
  * Also sets up new resources for the testing environment, such as the public
  * filesystem and configuration directories.
  *
  * This method is private as it must only be called once by TestBase::run()
  * (multiple invocations for the same test would have unpredictable
  * consequences) and it must not be callable or overridable by test classes.
  *
  * @see TestBase::beforePrepareEnvironment()
  */
 private function prepareEnvironment()
 {
     $user = \Drupal::currentUser();
     // Allow (base) test classes to backup global state information.
     $this->beforePrepareEnvironment();
     // Create the database prefix for this test.
     $this->prepareDatabasePrefix();
     $language_interface = \Drupal::languageManager()->getCurrentLanguage();
     // When running the test runner within a test, back up the original database
     // prefix.
     if (DRUPAL_TEST_IN_CHILD_SITE) {
         $this->originalPrefix = drupal_valid_test_ua();
     }
     // Backup current in-memory configuration.
     $site_path = \Drupal::service('site.path');
     $this->originalSite = $site_path;
     $this->originalSettings = Settings::getAll();
     $this->originalConfig = $GLOBALS['config'];
     // @todo Remove all remnants of $GLOBALS['conf'].
     // @see https://www.drupal.org/node/2183323
     $this->originalConf = isset($GLOBALS['conf']) ? $GLOBALS['conf'] : NULL;
     // Backup statics and globals.
     $this->originalContainer = clone \Drupal::getContainer();
     $this->originalLanguage = $language_interface;
     $this->originalConfigDirectories = $GLOBALS['config_directories'];
     // Save further contextual information.
     // Use the original files directory to avoid nesting it within an existing
     // simpletest directory if a test is executed within a test.
     $this->originalFileDirectory = Settings::get('file_public_path', $site_path . '/files');
     $this->originalProfile = drupal_get_profile();
     $this->originalUser = isset($user) ? clone $user : NULL;
     // Prevent that session data is leaked into the UI test runner by closing
     // the session and then setting the session-name (i.e. the name of the
     // session cookie) to a random value. If a test starts a new session, then
     // it will be associated with a different session-name. After the test-run
     // it can be safely destroyed.
     // @see TestBase::restoreEnvironment()
     if (PHP_SAPI !== 'cli' && session_status() === PHP_SESSION_ACTIVE) {
         session_write_close();
     }
     $this->originalSessionName = session_name();
     session_name('SIMPLETEST' . Crypt::randomBytesBase64());
     // Save and clean the shutdown callbacks array because it is static cached
     // and will be changed by the test run. Otherwise it will contain callbacks
     // from both environments and the testing environment will try to call the
     // handlers defined by the original one.
     $callbacks =& drupal_register_shutdown_function();
     $this->originalShutdownCallbacks = $callbacks;
     $callbacks = array();
     // Create test directory ahead of installation so fatal errors and debug
     // information can be logged during installation process.
     file_prepare_directory($this->siteDirectory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
     // Prepare filesystem directory paths.
     $this->publicFilesDirectory = $this->siteDirectory . '/files';
     $this->privateFilesDirectory = $this->siteDirectory . '/private';
     $this->tempFilesDirectory = $this->siteDirectory . '/temp';
     $this->translationFilesDirectory = $this->siteDirectory . '/translations';
     $this->generatedTestFiles = FALSE;
     // Ensure the configImporter is refreshed for each test.
     $this->configImporter = NULL;
     // Unregister all custom stream wrappers of the parent site.
     // Availability of Drupal stream wrappers varies by test base class:
     // - KernelTestBase supports and maintains stream wrappers in a custom
     //   way.
     // - WebTestBase re-initializes Drupal stream wrappers after installation.
     // The original stream wrappers are restored after the test run.
     // @see TestBase::restoreEnvironment()
     $this->originalContainer->get('stream_wrapper_manager')->unregister();
     // Reset statics.
     drupal_static_reset();
     // Ensure there is no service container.
     $this->container = NULL;
     \Drupal::unsetContainer();
     // Unset globals.
     unset($GLOBALS['config_directories']);
     unset($GLOBALS['config']);
     unset($GLOBALS['conf']);
     // Log fatal errors.
     ini_set('log_errors', 1);
     ini_set('error_log', DRUPAL_ROOT . '/' . $this->siteDirectory . '/error.log');
     // Change the database prefix.
     $this->changeDatabasePrefix();
     // After preparing the environment and changing the database prefix, we are
     // in a valid test environment.
     drupal_valid_test_ua($this->databasePrefix);
     // Reset settings.
     new Settings(array('hash_salt' => $this->databasePrefix, 'container_yamls' => []));
     drupal_set_time_limit($this->timeLimit);
 }
Example #21
0
 /**
  * {@inheritdoc}
  */
 public function prepareForm($form_id, &$form, FormStateInterface &$form_state)
 {
     $user = $this->currentUser();
     $form['#type'] = 'form';
     // Only update the action if it is not already set.
     if (!isset($form['#action'])) {
         $form['#action'] = $this->buildFormAction();
     }
     // Fix the form method, if it is 'get' in $form_state, but not in $form.
     if ($form_state->isMethodType('get') && !isset($form['#method'])) {
         $form['#method'] = 'get';
     }
     // Generate a new #build_id for this form, if none has been set already.
     // The form_build_id is used as key to cache a particular build of the form.
     // For multi-step forms, this allows the user to go back to an earlier
     // build, make changes, and re-submit.
     // @see self::buildForm()
     // @see self::rebuildForm()
     if (!isset($form['#build_id'])) {
         $form['#build_id'] = 'form-' . Crypt::randomBytesBase64();
     }
     $form['form_build_id'] = array('#type' => 'hidden', '#value' => $form['#build_id'], '#id' => $form['#build_id'], '#name' => 'form_build_id', '#parents' => array('form_build_id'));
     // Add a token, based on either #token or form_id, to any form displayed to
     // authenticated users. This ensures that any submitted form was actually
     // requested previously by the user and protects against cross site request
     // forgeries.
     // This does not apply to programmatically submitted forms. Furthermore,
     // since tokens are session-bound and forms displayed to anonymous users are
     // very likely cached, we cannot assign a token for them.
     // During installation, there is no $user yet.
     if ($user && $user->isAuthenticated() && !$form_state->isProgrammed()) {
         // Form constructors may explicitly set #token to FALSE when cross site
         // request forgery is irrelevant to the form, such as search forms.
         if (isset($form['#token']) && $form['#token'] === FALSE) {
             unset($form['#token']);
         } else {
             $form['#token'] = $form_id;
             $form['form_token'] = array('#id' => Html::getUniqueId('edit-' . $form_id . '-form-token'), '#type' => 'token', '#default_value' => $this->csrfToken->get($form['#token']), '#parents' => array('form_token'));
         }
     }
     if (isset($form_id)) {
         $form['form_id'] = array('#type' => 'hidden', '#value' => $form_id, '#id' => Html::getUniqueId("edit-{$form_id}"), '#parents' => array('form_id'));
     }
     if (!isset($form['#id'])) {
         $form['#id'] = Html::getUniqueId($form_id);
         // Provide a selector usable by JavaScript. As the ID is unique, its not
         // possible to rely on it in JavaScript.
         $form['#attributes']['data-drupal-selector'] = Html::getId($form_id);
     }
     $form += $this->elementInfo->getInfo('form');
     $form += array('#tree' => FALSE, '#parents' => array());
     $form['#validate'][] = '::validateForm';
     $form['#submit'][] = '::submitForm';
     $build_info = $form_state->getBuildInfo();
     // If no #theme has been set, automatically apply theme suggestions.
     // The form theme hook itself, which is rendered by form.html.twig,
     // is in #theme_wrappers. Therefore, the #theme function only has to care
     // for rendering the inner form elements, not the form itself.
     if (!isset($form['#theme'])) {
         $form['#theme'] = array($form_id);
         if (isset($build_info['base_form_id'])) {
             $form['#theme'][] = $build_info['base_form_id'];
         }
     }
     // Invoke hook_form_alter(), hook_form_BASE_FORM_ID_alter(), and
     // hook_form_FORM_ID_alter() implementations.
     $hooks = array('form');
     if (isset($build_info['base_form_id'])) {
         $hooks[] = 'form_' . $build_info['base_form_id'];
     }
     $hooks[] = 'form_' . $form_id;
     $this->moduleHandler->alter($hooks, $form, $form_state, $form_id);
     $this->themeManager->alter($hooks, $form, $form_state, $form_id);
 }
Example #22
0
 /**
  * {@inheritdoc}
  */
 public function prepareForm($form_id, &$form, FormStateInterface &$form_state)
 {
     $user = $this->currentUser();
     $form['#type'] = 'form';
     // Only update the action if it is not already set.
     if (!isset($form['#action'])) {
         // Instead of setting an actual action URL, we set the placeholder, which
         // will be replaced at the very last moment. This ensures forms with
         // dynamically generated action URLs don't have poor cacheability.
         // Use the proper API to generate the placeholder, when we have one. See
         // https://www.drupal.org/node/2562341.
         $placeholder = 'form_action_' . hash('crc32b', __METHOD__);
         $form['#attached']['placeholders'][$placeholder] = ['#lazy_builder' => ['form_builder:renderPlaceholderFormAction', []]];
         $form['#action'] = $placeholder;
     }
     // Fix the form method, if it is 'get' in $form_state, but not in $form.
     if ($form_state->isMethodType('get') && !isset($form['#method'])) {
         $form['#method'] = 'get';
     }
     // GET forms should not use a CSRF token.
     if (isset($form['#method']) && $form['#method'] === 'get') {
         // Merges in a default, this means if you've explicitly set #token to the
         // the $form_id on a GET form, which we don't recommend, it will work.
         $form += ['#token' => FALSE];
     }
     // Generate a new #build_id for this form, if none has been set already.
     // The form_build_id is used as key to cache a particular build of the form.
     // For multi-step forms, this allows the user to go back to an earlier
     // build, make changes, and re-submit.
     // @see self::buildForm()
     // @see self::rebuildForm()
     if (!isset($form['#build_id'])) {
         $form['#build_id'] = 'form-' . Crypt::randomBytesBase64();
     }
     $form['form_build_id'] = array('#type' => 'hidden', '#value' => $form['#build_id'], '#id' => $form['#build_id'], '#name' => 'form_build_id', '#parents' => array('form_build_id'));
     // Add a token, based on either #token or form_id, to any form displayed to
     // authenticated users. This ensures that any submitted form was actually
     // requested previously by the user and protects against cross site request
     // forgeries.
     // This does not apply to programmatically submitted forms. Furthermore,
     // since tokens are session-bound and forms displayed to anonymous users are
     // very likely cached, we cannot assign a token for them.
     // During installation, there is no $user yet.
     // Form constructors may explicitly set #token to FALSE when cross site
     // request forgery is irrelevant to the form, such as search forms.
     if ($form_state->isProgrammed() || isset($form['#token']) && $form['#token'] === FALSE) {
         unset($form['#token']);
     } else {
         $form['#cache']['contexts'][] = 'user.roles:authenticated';
         if ($user && $user->isAuthenticated()) {
             // Generate a public token based on the form id.
             $form['#token'] = $form_id;
             $form['form_token'] = array('#id' => Html::getUniqueId('edit-' . $form_id . '-form-token'), '#type' => 'token', '#default_value' => $this->csrfToken->get($form['#token']), '#parents' => array('form_token'), '#cache' => ['max-age' => 0]);
         }
     }
     if (isset($form_id)) {
         $form['form_id'] = array('#type' => 'hidden', '#value' => $form_id, '#id' => Html::getUniqueId("edit-{$form_id}"), '#parents' => array('form_id'));
     }
     if (!isset($form['#id'])) {
         $form['#id'] = Html::getUniqueId($form_id);
         // Provide a selector usable by JavaScript. As the ID is unique, its not
         // possible to rely on it in JavaScript.
         $form['#attributes']['data-drupal-selector'] = Html::getId($form_id);
     }
     $form += $this->elementInfo->getInfo('form');
     $form += array('#tree' => FALSE, '#parents' => array());
     $form['#validate'][] = '::validateForm';
     $form['#submit'][] = '::submitForm';
     $build_info = $form_state->getBuildInfo();
     // If no #theme has been set, automatically apply theme suggestions.
     // The form theme hook itself, which is rendered by form.html.twig,
     // is in #theme_wrappers. Therefore, the #theme function only has to care
     // for rendering the inner form elements, not the form itself.
     if (!isset($form['#theme'])) {
         $form['#theme'] = array($form_id);
         if (isset($build_info['base_form_id'])) {
             $form['#theme'][] = $build_info['base_form_id'];
         }
     }
     // Invoke hook_form_alter(), hook_form_BASE_FORM_ID_alter(), and
     // hook_form_FORM_ID_alter() implementations.
     $hooks = array('form');
     if (isset($build_info['base_form_id'])) {
         $hooks[] = 'form_' . $build_info['base_form_id'];
     }
     $hooks[] = 'form_' . $form_id;
     $this->moduleHandler->alter($hooks, $form, $form_state, $form_id);
     $this->themeManager->alter($hooks, $form, $form_state, $form_id);
 }
 /**
  * Add settings that are missed since the installer isn't run.
  */
 protected function prepareSettings()
 {
     parent::prepareSettings();
     // Remember the profile which was used.
     $settings['settings']['install_profile'] = (object) ['value' => $this->installProfile, 'required' => TRUE];
     // Generate a hash salt.
     $settings['settings']['hash_salt'] = (object) ['value' => Crypt::randomBytesBase64(55), 'required' => TRUE];
     // Since the installer isn't run, add the database settings here too.
     $settings['databases']['default'] = (object) ['value' => Database::getConnectionInfo(), 'required' => TRUE];
     $this->writeSettings($settings);
 }
 /**
  * Sends no-JS BigPipe placeholders' replacements as embedded HTML responses.
  *
  * @param string $html
  *   HTML markup.
  * @param array $no_js_placeholders
  *   Associative array; the no-JS BigPipe placeholders. Keys are the BigPipe
  *   selectors.
  * @param \Drupal\Core\Asset\AttachedAssetsInterface $cumulative_assets
  *   The cumulative assets sent so far; to be updated while rendering no-JS
  *   BigPipe placeholders.
  *
  * @throws \Exception
  *   If an exception is thrown during the rendering of a placeholder, it is
  *   caught to allow the other placeholders to still be replaced. But when
  *   error logging is configured to be verbose, the exception is rethrown to
  *   simplify debugging.
  */
 protected function sendNoJsPlaceholders($html, $no_js_placeholders, AttachedAssetsInterface $cumulative_assets)
 {
     // Split the HTML on every no-JS placeholder string.
     $prepare_for_preg_split = function ($placeholder_string) {
         return '(' . preg_quote($placeholder_string, '/') . ')';
     };
     $preg_placeholder_strings = array_map($prepare_for_preg_split, array_keys($no_js_placeholders));
     $fragments = preg_split('/' . implode('|', $preg_placeholder_strings) . '/', $html, NULL, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
     foreach ($fragments as $fragment) {
         // If the fragment isn't one of the no-JS placeholders, it is the HTML in
         // between placeholders and it must be printed & flushed immediately. The
         // rest of the logic in the loop handles the placeholders.
         if (!isset($no_js_placeholders[$fragment])) {
             print $fragment;
             flush();
             continue;
         }
         $placeholder = $fragment;
         assert('isset($no_js_placeholders[$placeholder])');
         $token = Crypt::randomBytesBase64(55);
         // Render the placeholder, but include the cumulative settings assets, so
         // we can calculate the overall settings for the entire page.
         $placeholder_plus_cumulative_settings = ['placeholder' => $no_js_placeholders[$placeholder], 'cumulative_settings_' . $token => ['#attached' => ['drupalSettings' => $cumulative_assets->getSettings()]]];
         try {
             $elements = $this->renderPlaceholder($placeholder, $placeholder_plus_cumulative_settings);
         } catch (\Exception $e) {
             if (\Drupal::config('system.logging')->get('error_level') === ERROR_REPORTING_DISPLAY_VERBOSE) {
                 throw $e;
             } else {
                 trigger_error($e, E_USER_ERROR);
                 continue;
             }
         }
         // Create a new HtmlResponse. Ensure the CSS and (non-bottom) JS is sent
         // before the HTML they're associated with. In other words: ensure the
         // critical assets for this placeholder's markup are loaded first.
         // @see \Drupal\Core\Render\HtmlResponseSubscriber
         // @see template_preprocess_html()
         $css_placeholder = '<nojs-bigpipe-placeholder-styles-placeholder token="' . $token . '">';
         $js_placeholder = '<nojs-bigpipe-placeholder-scripts-placeholder token="' . $token . '">';
         $elements['#markup'] = BigPipeMarkup::create($css_placeholder . $js_placeholder . (string) $elements['#markup']);
         $elements['#attached']['html_response_attachment_placeholders']['styles'] = $css_placeholder;
         $elements['#attached']['html_response_attachment_placeholders']['scripts'] = $js_placeholder;
         $html_response = new HtmlResponse();
         $html_response->setContent($elements);
         $html_response->getCacheableMetadata()->setCacheMaxAge(0);
         // Push a fake request with the asset libraries loaded so far and dispatch
         // KernelEvents::RESPONSE event. This results in the attachments for the
         // HTML response being processed by HtmlResponseAttachmentsProcessor and
         // hence:
         // - the HTML to load the CSS can be rendered.
         // - the HTML to load the JS (at the top) can be rendered.
         $fake_request = $this->requestStack->getMasterRequest()->duplicate();
         $fake_request->request->set('ajax_page_state', ['libraries' => implode(',', $cumulative_assets->getAlreadyLoadedLibraries())]);
         try {
             $html_response = $this->filterEmbeddedResponse($fake_request, $html_response);
         } catch (\Exception $e) {
             if (\Drupal::config('system.logging')->get('error_level') === ERROR_REPORTING_DISPLAY_VERBOSE) {
                 throw $e;
             } else {
                 trigger_error($e, E_USER_ERROR);
                 continue;
             }
         }
         // Send this embedded HTML response.
         print $html_response->getContent();
         flush();
         // Another placeholder was rendered and sent, track the set of asset
         // libraries sent so far. Any new settings also need to be tracked, so
         // they can be sent in ::sendPreBody().
         $cumulative_assets->setAlreadyLoadedLibraries(array_merge($cumulative_assets->getAlreadyLoadedLibraries(), $html_response->getAttachments()['library']));
         $cumulative_assets->setSettings($html_response->getAttachments()['drupalSettings']);
     }
 }
 /**
  * {@inheritdoc}
  */
 public function submitForm(array &$form, FormStateInterface $form_state)
 {
     global $install_state;
     // Update global settings array and save.
     $settings = array();
     $database = $form_state->get('database');
     $settings['databases']['default']['default'] = (object) array('value' => $database, 'required' => TRUE);
     $settings['settings']['hash_salt'] = (object) array('value' => Crypt::randomBytesBase64(55), 'required' => TRUE);
     // Remember the profile which was used.
     $settings['settings']['install_profile'] = (object) array('value' => $install_state['parameters']['profile'], 'required' => TRUE);
     drupal_rewrite_settings($settings);
     // Add the config directories to settings.php.
     drupal_install_config_directories();
     // Indicate that the settings file has been verified, and check the database
     // for the last completed task, now that we have a valid connection. This
     // last step is important since we want to trigger an error if the new
     // database already has Drupal installed.
     $install_state['settings_verified'] = TRUE;
     $install_state['config_verified'] = TRUE;
     $install_state['database_verified'] = TRUE;
     $install_state['completed_task'] = install_verify_completed_task();
 }
 /**
  * Test to be run and the results confirmed.
  *
  * Here we force test results which must match the expected results from
  * confirmStubResults().
  */
 function stubTest()
 {
     // Ensure the .htkey file exists since this is only created just before a
     // request. This allows the stub test to make requests. The event does not
     // fire here and drupal_generate_test_ua() can not generate a key for a
     // test in a test since the prefix has changed.
     // @see \Drupal\Core\Test\EventSubscriber\HttpRequestSubscriber::onBeforeSendRequest()
     // @see drupal_generate_test_ua();
     $key_file = DRUPAL_ROOT . '/sites/simpletest/' . substr($this->databasePrefix, 10) . '/.htkey';
     $private_key = Crypt::randomBytesBase64(55);
     $site_path = $this->container->get('site.path');
     file_put_contents($key_file, $private_key);
     // This causes the first of the fifteen passes asserted in
     // confirmStubResults().
     $this->pass($this->passMessage);
     // The first three fails are caused by enabling a non-existent module in
     // setUp().
     // This causes the fourth of the five fails asserted in
     // confirmStubResults().
     $this->fail($this->failMessage);
     // This causes the second to fourth of the fifteen passes asserted in
     // confirmStubResults().
     $user = $this->drupalCreateUser(array($this->validPermission), 'SimpleTestTest');
     // This causes the fifth of the five fails asserted in confirmStubResults().
     $this->drupalCreateUser(array($this->invalidPermission));
     // Test logging in as a user.
     // This causes the fifth to ninth of the fifteen passes asserted in
     // confirmStubResults().
     $this->drupalLogin($user);
     // This causes the tenth of the fifteen passes asserted in
     // confirmStubResults().
     $this->pass(t('Test ID is @id.', array('@id' => $this->testId)));
     // These cause the eleventh to fourteenth of the fifteen passes asserted in
     // confirmStubResults().
     $this->assertTrue(file_exists($site_path . '/settings.testing.php'));
     // Check the settings.testing.php file got included.
     $this->assertTrue(function_exists('simpletest_test_stub_settings_function'));
     // Check that the test-specific service file got loaded.
     $this->assertTrue($this->container->has('site.service.yml'));
     $this->assertIdentical(get_class($this->container->get('cache.backend.database')), 'Drupal\\Core\\Cache\\MemoryBackendFactory');
     // These cause the two exceptions asserted in confirmStubResults().
     // Call trigger_error() without the required argument to trigger an E_WARNING.
     trigger_error();
     // Generates a warning inside a PHP function.
     array_key_exists(NULL, NULL);
     // This causes the fifteenth of the fifteen passes asserted in
     // confirmStubResults().
     $this->assertNothing();
     // This causes the debug message asserted in confirmStubResults().
     debug('Foo', 'Debug', FALSE);
 }
Example #27
0
 /**
  * Validates user, hash, and timestamp; logs the user in if correct.
  *
  * @param int $uid
  *   User ID of the user requesting reset.
  * @param int $timestamp
  *   The current timestamp.
  * @param string $hash
  *   Login link hash.
  *
  * @return \Symfony\Component\HttpFoundation\RedirectResponse
  *   Returns a redirect to the user edit form if the information is correct.
  *   If the information is incorrect redirects to 'user.pass' route with a
  *   message for the user.
  *
  * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
  *   If $uid is for a blocked user or invalid user ID.
  */
 public function resetPassLogin($uid, $timestamp, $hash)
 {
     // The current user is not logged in, so check the parameters.
     $current = REQUEST_TIME;
     /** @var \Drupal\user\UserInterface $user */
     $user = $this->userStorage->load($uid);
     // Verify that the user exists and is active.
     if ($user === NULL || !$user->isActive()) {
         // Blocked or invalid user ID, so deny access. The parameters will be in
         // the watchdog's URL for the administrator to check.
         throw new AccessDeniedHttpException();
     }
     // Time out, in seconds, until login URL expires.
     $timeout = $this->config('user.settings')->get('password_reset_timeout');
     // No time out for first time login.
     if ($user->getLastLoginTime() && $current - $timestamp > $timeout) {
         drupal_set_message($this->t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'), 'error');
         return $this->redirect('user.pass');
     } elseif ($user->isAuthenticated() && $timestamp >= $user->getLastLoginTime() && $timestamp <= $current && Crypt::hashEquals($hash, user_pass_rehash($user, $timestamp))) {
         user_login_finalize($user);
         $this->logger->notice('User %name used one-time login link at time %timestamp.', ['%name' => $user->getDisplayName(), '%timestamp' => $timestamp]);
         drupal_set_message($this->t('You have just used your one-time login link. It is no longer necessary to use this link to log in. Please change your password.'));
         // Let the user's password be changed without the current password
         // check.
         $token = Crypt::randomBytesBase64(55);
         $_SESSION['pass_reset_' . $user->id()] = $token;
         return $this->redirect('entity.user.edit_form', ['user' => $user->id()], ['query' => ['pass-reset-token' => $token], 'absolute' => TRUE]);
     }
     drupal_set_message($this->t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'), 'error');
     return $this->redirect('user.pass');
 }
Example #28
0
 /**
  * {@inheritdoc}
  */
 public function getCache($form_build_id, FormStateInterface $form_state)
 {
     if ($form = $this->keyValueExpirableFactory->get('form')->get($form_build_id)) {
         if (isset($form['#cache_token']) && $this->csrfToken->validate($form['#cache_token']) || !isset($form['#cache_token']) && $this->currentUser->isAnonymous()) {
             $this->loadCachedFormState($form_build_id, $form_state);
             // Generate a new #build_id if the cached form was rendered on a
             // cacheable page.
             $build_info = $form_state->getBuildInfo();
             if (!empty($build_info['immutable'])) {
                 $form['#build_id_old'] = $form['#build_id'];
                 $form['#build_id'] = 'form-' . Crypt::randomBytesBase64();
                 $form['form_build_id']['#value'] = $form['#build_id'];
                 $form['form_build_id']['#id'] = $form['#build_id'];
                 unset($build_info['immutable']);
                 $form_state->setBuildInfo($build_info);
             }
             return $form;
         }
     }
 }
Example #29
0
 /**
  * Ensures that this subscription has a valid token.
  */
 protected function ensureToken()
 {
     if (!$this->getToken()) {
         $this->set('token', substr(Crypt::randomBytesBase64(20), 0, 20));
     }
 }