/**
  * Autocomplete the label of an entity.
  *
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The request object that contains the typed tags.
  * @param string $target_type
  *   The ID of the target entity type.
  * @param string $selection_handler
  *   The plugin ID of the entity reference selection handler.
  * @param string $selection_settings_key
  *   The hashed key of the key/value entry that holds the selection handler
  *   settings.
  *
  * @return \Symfony\Component\HttpFoundation\JsonResponse
  *   The matched entity labels as a JSON response.
  *
  * @throws \Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException
  *   Thrown if the selection settings key is not found in the key/value store
  *   or if it does not match the stored data.
  */
 public function handleAutocomplete(Request $request, $target_type, $selection_handler, $selection_settings_key)
 {
     $matches = array();
     // Get the typed string from the URL, if it exists.
     if ($input = $request->query->get('q')) {
         $typed_string = Tags::explode($input);
         $typed_string = Unicode::strtolower(array_pop($typed_string));
         // Selection settings are passed in as a hashed key of a serialized array
         // stored in the key/value store.
         $selection_settings = $this->keyValue->get($selection_settings_key, FALSE);
         if ($selection_settings !== FALSE) {
             $selection_settings_hash = Crypt::hmacBase64(serialize($selection_settings) . $target_type . $selection_handler, Settings::getHashSalt());
             if ($selection_settings_hash !== $selection_settings_key) {
                 // Disallow access when the selection settings hash does not match the
                 // passed-in key.
                 throw new AccessDeniedHttpException('Invalid selection settings key.');
             }
         } else {
             // Disallow access when the selection settings key is not found in the
             // key/value store.
             throw new AccessDeniedHttpException();
         }
         $matches = $this->matcher->getMatches($target_type, $selection_handler, $selection_settings, $typed_string);
     }
     return new JsonResponse($matches);
 }
 /**
  * #pre_render callback to generate a placeholder.
  *
  * Ensures the same token is used for all instances, hence resulting in the
  * same placeholder for all places rendering the status messages for this
  * request (e.g. in multiple blocks). This ensures we can put the rendered
  * messages in all placeholders in one go.
  * Also ensures the same context key is used for the #post_render_cache
  * property, this ensures that if status messages are rendered multiple times,
  * their individual (but identical!) #post_render_cache properties are merged,
  * ensuring the callback is only invoked once.
  *
  * @see ::renderMessages()
  * @param array $element
  *   A renderable array.
  *
  * @return array
  *   The updated renderable array containing the placeholder.
  */
 public static function generatePlaceholder(array $element)
 {
     $plugin_id = 'status_messages';
     $callback = get_class() . '::renderMessages';
     try {
         $hash_salt = Settings::getHashSalt();
     } catch (\RuntimeException $e) {
         // Status messages are also shown during the installer, at which time no
         // hash salt is defined yet.
         $hash_salt = Crypt::randomBytes(8);
     }
     $key = $plugin_id . $element['#display'];
     $context = ['display' => $element['#display'], 'token' => Crypt::hmacBase64($key, $hash_salt)];
     $placeholder = static::renderer()->generateCachePlaceholder($callback, $context);
     $element['#post_render_cache'] = [$callback => [$key => $context]];
     $element['#markup'] = $placeholder;
     return $element;
 }
Example #3
0
 /**
  * @covers ::getPathToken
  */
 public function testGetPathToken()
 {
     $logger = $this->getMockBuilder('\\Psr\\Log\\LoggerInterface')->getMock();
     $private_key = $this->randomMachineName();
     $hash_salt = $this->randomMachineName();
     // Image style that changes the extension.
     $image_effect_id = $this->randomMachineName();
     $image_effect = $this->getMockBuilder('\\Drupal\\image\\ImageEffectBase')->setConstructorArgs(array(array(), $image_effect_id, array(), $logger))->getMock();
     $image_effect->expects($this->any())->method('getDerivativeExtension')->will($this->returnValue('png'));
     $image_style = $this->getImageStyleMock($image_effect_id, $image_effect, array('getPrivateKey', 'getHashSalt'));
     $image_style->expects($this->any())->method('getPrivateKey')->will($this->returnValue($private_key));
     $image_style->expects($this->any())->method('getHashSalt')->will($this->returnValue($hash_salt));
     // Assert the extension has been added to the URI before creating the token.
     $this->assertEquals($image_style->getPathToken('public://test.jpeg.png'), $image_style->getPathToken('public://test.jpeg'));
     $this->assertEquals(substr(Crypt::hmacBase64($image_style->id() . ':' . 'public://test.jpeg.png', $private_key . $hash_salt), 0, 8), $image_style->getPathToken('public://test.jpeg'));
     $this->assertNotEquals(substr(Crypt::hmacBase64($image_style->id() . ':' . 'public://test.jpeg', $private_key . $hash_salt), 0, 8), $image_style->getPathToken('public://test.jpeg'));
     // Image style that doesn't change the extension.
     $image_effect_id = $this->randomMachineName();
     $image_effect = $this->getMockBuilder('\\Drupal\\image\\ImageEffectBase')->setConstructorArgs(array(array(), $image_effect_id, array(), $logger))->getMock();
     $image_effect->expects($this->any())->method('getDerivativeExtension')->will($this->returnArgument(0));
     $image_style = $this->getImageStyleMock($image_effect_id, $image_effect, array('getPrivateKey', 'getHashSalt'));
     $image_style->expects($this->any())->method('getPrivateKey')->will($this->returnValue($private_key));
     $image_style->expects($this->any())->method('getHashSalt')->will($this->returnValue($hash_salt));
     // Assert no extension has been added to the uri before creating the token.
     $this->assertNotEquals($image_style->getPathToken('public://test.jpeg.png'), $image_style->getPathToken('public://test.jpeg'));
     $this->assertNotEquals(substr(Crypt::hmacBase64($image_style->id() . ':' . 'public://test.jpeg.png', $private_key . $hash_salt), 0, 8), $image_style->getPathToken('public://test.jpeg'));
     $this->assertEquals(substr(Crypt::hmacBase64($image_style->id() . ':' . 'public://test.jpeg', $private_key . $hash_salt), 0, 8), $image_style->getPathToken('public://test.jpeg'));
 }
 /**
  * Returns the result of an Entity reference autocomplete request.
  *
  * @param string $input
  *   The label of the entity to query by.
  *
  * @return mixed
  *  The JSON value encoded in its appropriate PHP type.
  */
 protected function getAutocompleteResult($input)
 {
     $request = Request::create('entity_reference_autocomplete/' . $this->entityType . '/default');
     $request->query->set('q', $input);
     $selection_settings = [];
     $selection_settings_key = Crypt::hmacBase64(serialize($selection_settings) . $this->entityType . 'default', Settings::getHashSalt());
     \Drupal::keyValue('entity_autocomplete')->set($selection_settings_key, $selection_settings);
     $entity_reference_controller = EntityAutocompleteController::create($this->container);
     $result = $entity_reference_controller->handleAutocomplete($request, $this->entityType, 'default', $selection_settings_key)->getContent();
     return Json::decode($result);
 }
 /**
  * Adds entity autocomplete functionality to a form element.
  *
  * @param array $element
  *   The form element to process. Properties used:
  *   - #target_type: The ID of the target entity type.
  *   - #selection_handler: The plugin ID of the entity reference selection
  *     handler.
  *   - #selection_settings: An array of settings that will be passed to the
  *     selection handler.
  * @param \Drupal\Core\Form\FormStateInterface $form_state
  *   The current state of the form.
  * @param array $complete_form
  *   The complete form structure.
  *
  * @return array
  *   The form element.
  *
  * @throws \InvalidArgumentException
  *   Exception thrown when the #target_type or #autocreate['bundle'] are
  *   missing.
  */
 public static function processEntityAutocomplete(array &$element, FormStateInterface $form_state, array &$complete_form)
 {
     // Nothing to do if there is no target entity type.
     if (empty($element['#target_type'])) {
         throw new \InvalidArgumentException('Missing required #target_type parameter.');
     }
     // Provide default values and sanity checks for the #autocreate parameter.
     if ($element['#autocreate']) {
         if (!isset($element['#autocreate']['bundle'])) {
             throw new \InvalidArgumentException("Missing required #autocreate['bundle'] parameter.");
         }
         // Default the autocreate user ID to the current user.
         $element['#autocreate']['uid'] = isset($element['#autocreate']['uid']) ? $element['#autocreate']['uid'] : \Drupal::currentUser()->id();
     }
     // Store the selection settings in the key/value store and pass a hashed key
     // in the route parameters.
     $selection_settings = isset($element['#selection_settings']) ? $element['#selection_settings'] : [];
     $data = serialize($selection_settings) . $element['#target_type'] . $element['#selection_handler'];
     $selection_settings_key = Crypt::hmacBase64($data, Settings::getHashSalt());
     $key_value_storage = \Drupal::keyValue('entity_autocomplete');
     if (!$key_value_storage->has($selection_settings_key)) {
         $key_value_storage->set($selection_settings_key, $selection_settings);
     }
     $element['#autocomplete_route_name'] = 'system.entity_autocomplete';
     $element['#autocomplete_route_parameters'] = array('target_type' => $element['#target_type'], 'selection_handler' => $element['#selection_handler'], 'selection_settings_key' => $selection_settings_key);
     return $element;
 }
Example #6
0
 /**
  * Generates a token based on $value, the token seed, and the private key.
  *
  * @param string $seed
  *   The per-session token seed.
  * @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()
  */
 protected function computeToken($seed, $value = '')
 {
     return Crypt::hmacBase64($value, $seed . $this->privateKey->get() . Settings::getHashSalt());
 }
 /**
  * {@inheritdoc}
  */
 public function getPathToken($uri)
 {
     // Return the first 8 characters.
     return substr(Crypt::hmacBase64($this->id() . ':' . $this->addExtension($uri), $this->getPrivateKey() . $this->getHashSalt()), 0, 8);
 }
Example #8
0
 /**
  * {@inheritdoc}
  */
 public function processFetchTask($project)
 {
     global $base_url;
     // This can be in the middle of a long-running batch, so REQUEST_TIME won't
     // necessarily be valid.
     $request_time_difference = time() - REQUEST_TIME;
     if (empty($this->failed)) {
         // If we have valid data about release history XML servers that we have
         // failed to fetch from on previous attempts, load that.
         $this->failed = $this->tempStore->get('fetch_failures');
     }
     $max_fetch_attempts = $this->updateSettings->get('fetch.max_attempts');
     $success = FALSE;
     $available = array();
     $site_key = Crypt::hmacBase64($base_url, $this->privateKey->get());
     $fetch_url_base = $this->updateFetcher->getFetchBaseUrl($project);
     $project_name = $project['name'];
     if (empty($this->failed[$fetch_url_base]) || $this->failed[$fetch_url_base] < $max_fetch_attempts) {
         $data = $this->updateFetcher->fetchProjectData($project, $site_key);
     }
     if (!empty($data)) {
         $available = $this->parseXml($data);
         // @todo: Purge release data we don't need. See
         //   https://www.drupal.org/node/238950.
         if (!empty($available)) {
             // Only if we fetched and parsed something sane do we return success.
             $success = TRUE;
         }
     } else {
         $available['project_status'] = 'not-fetched';
         if (empty($this->failed[$fetch_url_base])) {
             $this->failed[$fetch_url_base] = 1;
         } else {
             $this->failed[$fetch_url_base]++;
         }
     }
     $frequency = $this->updateSettings->get('check.interval_days');
     $available['last_fetch'] = REQUEST_TIME + $request_time_difference;
     $this->availableReleasesTempStore->setWithExpire($project_name, $available, $request_time_difference + 60 * 60 * 24 * $frequency);
     // Stash the $this->failed data back in the DB for the next 5 minutes.
     $this->tempStore->setWithExpire('fetch_failures', $this->failed, $request_time_difference + 60 * 5);
     // Whether this worked or not, we did just (try to) check for updates.
     $this->stateStore->set('update.last_check', REQUEST_TIME + $request_time_difference);
     // Now that we processed the fetch task for this project, clear out the
     // record for this task so we're willing to fetch again.
     $this->fetchTaskStore->delete($project_name);
     return $success;
 }
Example #9
0
 /**
  * {@inheritdoc}
  */
 public function getPathToken($uri)
 {
     // Return the first 8 characters.
     return substr(Crypt::hmacBase64($this->id() . ':' . $uri, \Drupal::service('private_key')->get() . Settings::getHashSalt()), 0, 8);
 }
Example #10
0
<?php

/**
 * @file
 * Rebuilds all Drupal caches even when Drupal itself does not work.
 *
 * Needs a token query argument which can be calculated using the
 * scripts/rebuild_token_calculator.sh script.
 *
 * @see drupal_rebuild()
 */
use Drupal\Component\Utility\Crypt;
use Drupal\Core\DrupalKernel;
use Drupal\Core\Site\Settings;
use Symfony\Component\HttpFoundation\Request;
// Change the directory to the Drupal root.
chdir('..');
$autoloader = (require_once __DIR__ . '/vendor/autoload.php');
require_once __DIR__ . '/includes/utility.inc';
$request = Request::createFromGlobals();
// Manually resemble early bootstrap of DrupalKernel::boot().
require_once __DIR__ . '/includes/bootstrap.inc';
DrupalKernel::bootEnvironment();
Settings::initialize(DrupalKernel::findSitePath($request));
if (Settings::get('rebuild_access', FALSE) || $request->get('token') && $request->get('timestamp') && REQUEST_TIME - $request->get('timestamp') < 300 && $request->get('token') === Crypt::hmacBase64($request->get('timestamp'), Settings::get('hash_salt'))) {
    drupal_rebuild($autoloader, $request);
    drupal_set_message('Cache rebuild complete.');
}
$base_path = dirname(dirname($request->getBaseUrl()));
header('Location: ' . $base_path);
 /**
  * Tests the hmacBase64 method with invalid parameters.
  *
  * @param string $data
  *   Data to hash.
  * @param string $key
  *   Key to use in hashing process.
  *
  * @dataProvider providerTestHmacBase64Invalid
  * @expectedException InvalidArgumentException
  */
 public function testHmacBase64Invalid($data, $key)
 {
     Crypt::hmacBase64($data, $key);
 }
 /**
  * {@inheritdoc}
  */
 public function digest()
 {
     return Crypt::hmacBase64(Json::encode($this->getValues()), Settings::getHashSalt());
 }
Example #13
0
 /**
  * Render API callback: Expands the managed_file element type.
  *
  * Expands the file type to include Upload and Remove buttons, as well as
  * support for a default value.
  */
 public static function processManagedFile(&$element, FormStateInterface $form_state, &$complete_form)
 {
     // This is used sometimes so let's implode it just once.
     $parents_prefix = implode('_', $element['#parents']);
     $fids = isset($element['#value']['fids']) ? $element['#value']['fids'] : [];
     // Set some default element properties.
     $element['#progress_indicator'] = empty($element['#progress_indicator']) ? 'none' : $element['#progress_indicator'];
     $element['#files'] = !empty($fids) ? File::loadMultiple($fids) : FALSE;
     $element['#tree'] = TRUE;
     // Generate a unique wrapper HTML ID.
     $ajax_wrapper_id = Html::getUniqueId('ajax-wrapper');
     $ajax_settings = ['callback' => [get_called_class(), 'uploadAjaxCallback'], 'options' => ['query' => ['element_parents' => implode('/', $element['#array_parents'])]], 'wrapper' => $ajax_wrapper_id, 'effect' => 'fade', 'progress' => ['type' => $element['#progress_indicator'], 'message' => $element['#progress_message']]];
     // Set up the buttons first since we need to check if they were clicked.
     $element['upload_button'] = ['#name' => $parents_prefix . '_upload_button', '#type' => 'submit', '#value' => t('Upload'), '#attributes' => ['class' => ['js-hide']], '#validate' => [], '#submit' => ['file_managed_file_submit'], '#limit_validation_errors' => [$element['#parents']], '#ajax' => $ajax_settings, '#weight' => -5];
     // Force the progress indicator for the remove button to be either 'none' or
     // 'throbber', even if the upload button is using something else.
     $ajax_settings['progress']['type'] = $element['#progress_indicator'] == 'none' ? 'none' : 'throbber';
     $ajax_settings['progress']['message'] = NULL;
     $ajax_settings['effect'] = 'none';
     $element['remove_button'] = ['#name' => $parents_prefix . '_remove_button', '#type' => 'submit', '#value' => $element['#multiple'] ? t('Remove selected') : t('Remove'), '#validate' => [], '#submit' => ['file_managed_file_submit'], '#limit_validation_errors' => [$element['#parents']], '#ajax' => $ajax_settings, '#weight' => 1];
     $element['fids'] = ['#type' => 'hidden', '#value' => $fids];
     // Add progress bar support to the upload if possible.
     if ($element['#progress_indicator'] == 'bar' && ($implementation = file_progress_implementation())) {
         $upload_progress_key = mt_rand();
         if ($implementation == 'uploadprogress') {
             $element['UPLOAD_IDENTIFIER'] = ['#type' => 'hidden', '#value' => $upload_progress_key, '#attributes' => ['class' => ['file-progress']], '#weight' => -20];
         } elseif ($implementation == 'apc') {
             $element['APC_UPLOAD_PROGRESS'] = ['#type' => 'hidden', '#value' => $upload_progress_key, '#attributes' => ['class' => ['file-progress']], '#weight' => -20];
         }
         // Add the upload progress callback.
         $element['upload_button']['#ajax']['progress']['url'] = Url::fromRoute('file.ajax_progress', ['key' => $upload_progress_key]);
     }
     // The file upload field itself.
     $element['upload'] = ['#name' => 'files[' . $parents_prefix . ']', '#type' => 'file', '#title' => t('Choose a file'), '#title_display' => 'invisible', '#size' => $element['#size'], '#multiple' => $element['#multiple'], '#theme_wrappers' => [], '#weight' => -10, '#error_no_message' => TRUE];
     if (!empty($fids) && $element['#files']) {
         foreach ($element['#files'] as $delta => $file) {
             $file_link = ['#theme' => 'file_link', '#file' => $file];
             if ($element['#multiple']) {
                 $element['file_' . $delta]['selected'] = ['#type' => 'checkbox', '#title' => \Drupal::service('renderer')->renderPlain($file_link)];
             } else {
                 $element['file_' . $delta]['filename'] = $file_link + ['#weight' => -10];
             }
             // Anonymous users who have uploaded a temporary file need a
             // non-session-based token added so $this->valueCallback() can check
             // that they have permission to use this file on subsequent submissions
             // of the same form (for example, after an Ajax upload or form
             // validation error).
             if ($file->isTemporary() && \Drupal::currentUser()->isAnonymous()) {
                 $element['file_' . $delta]['fid_token'] = array('#type' => 'hidden', '#value' => Crypt::hmacBase64('file-' . $delta, \Drupal::service('private_key')->get() . Settings::getHashSalt()));
             }
         }
     }
     // Add the extension list to the page as JavaScript settings.
     if (isset($element['#upload_validators']['file_validate_extensions'][0])) {
         $extension_list = implode(',', array_filter(explode(' ', $element['#upload_validators']['file_validate_extensions'][0])));
         $element['upload']['#attached']['drupalSettings']['file']['elements']['#' . $element['#id']] = $extension_list;
     }
     // Let #id point to the file element, so the field label's 'for' corresponds
     // with it.
     $element['#id'] =& $element['upload']['#id'];
     // Prefix and suffix used for Ajax replacement.
     $element['#prefix'] = '<div id="' . $ajax_wrapper_id . '">';
     $element['#suffix'] = '</div>';
     return $element;
 }
Example #14
0
 /**
  * Generate a token for the currently logged in user.
  */
 protected function drupalGetToken($value = '')
 {
     $private_key = \Drupal::service('private_key')->get();
     return Crypt::hmacBase64($value, $this->session_id . $private_key);
 }
Example #15
0
 /**
  * Generates a key from job item data that can be used in the URL.
  *
  * @param \Drupal\tmgmt\JobItemInterface $tmgmt_job_item
  *   Job item.
  *
  * @return string
  *   Returns hashed key that is safe to use in the URL.
  */
 public function getKey(JobItemInterface $tmgmt_job_item)
 {
     return Crypt::hmacBase64($tmgmt_job_item->id(), Settings::getHashSalt());
 }
 /**
  * Stop-gap fix.
  *
  * @see http://drupal.org/node/1555862
  */
 protected function drupalGetToken($value = '')
 {
     // Use the same code as \Drupal\Core\Access\CsrfTokenGenerator::get().
     $private_key = $this->container->get('private_key')->get();
     /** @var \Drupal\Core\Session\MetadataBag $session_metadata */
     $session_metadata = $this->container->get('session_manager.metadata_bag');
     // @TODO Try to get seed from testing site, broken now.
     $seed = $session_metadata->getCsrfTokenSeed();
     return Crypt::hmacBase64($value, $seed . $private_key . Settings::getHashSalt());
 }