예제 #1
0
 /**
  * {@inheritdoc}
  */
 public function validateForm($form_id, &$form, &$form_state)
 {
     // If this form is flagged to always validate, ensure that previous runs of
     // validation are ignored.
     if (!empty($form_state['must_validate'])) {
         $form_state['validation_complete'] = FALSE;
     }
     // If this form has completed validation, do not validate again.
     if (!empty($form_state['validation_complete'])) {
         return;
     }
     // If the session token was set by self::prepareForm(), ensure that it
     // matches the current user's session.
     if (isset($form['#token'])) {
         if (!$this->csrfToken->validate($form_state['values']['form_token'], $form['#token'])) {
             $url = $this->requestStack->getCurrentRequest()->getRequestUri();
             // Setting this error will cause the form to fail validation.
             $this->setErrorByName('form_token', $form_state, $this->t('The form has become outdated. Copy any unsaved work in the form below and then <a href="@link">reload this page</a>.', array('@link' => $url)));
             // Stop here and don't run any further validation handlers, because they
             // could invoke non-safe operations which opens the door for CSRF
             // vulnerabilities.
             $this->finalizeValidation($form, $form_state, $form_id);
             return;
         }
     }
     // Recursively validate each form element.
     $this->doValidateForm($form, $form_state, $form_id);
     $this->finalizeValidation($form, $form_state, $form_id);
     $this->handleErrorsWithLimitedValidation($form, $form_state, $form_id);
 }
  /**
   * Transliterates a string in given language. Various postprocessing possible.
   *
   * @param \Symfony\Component\HttpFoundation\Request $request
   *   The input string and language for the transliteration.
   *   Optionally may contain the replace_pattern, replace, lowercase parameters.
   *
   * @return \Symfony\Component\HttpFoundation\JsonResponse
   *   The transliterated string.
   */
  public function transliterate(Request $request) {
    $text = $request->query->get('text');
    $langcode = $request->query->get('langcode');
    $replace_pattern = $request->query->get('replace_pattern');
    $replace_token = $request->query->get('replace_token');
    $replace = $request->query->get('replace');
    $lowercase = $request->query->get('lowercase');

    $transliterated = $this->transliteration->transliterate($text, $langcode, '_');
    if ($lowercase) {
      $transliterated = Unicode::strtolower($transliterated);
    }

    if (isset($replace_pattern) && isset($replace)) {
      if (!isset($replace_token)) {
        throw new AccessDeniedException("Missing 'replace_token' query parameter.");
      }
      elseif (!$this->tokenGenerator->validate($replace_token, $replace_pattern)) {
        throw new AccessDeniedException("Invalid 'replace_token' query parameter.");
      }

      // Quote the pattern delimiter and remove null characters to avoid the e
      // or other modifiers being injected.
      $transliterated = preg_replace('@' . strtr($replace_pattern, ['@' => '\@', chr(0) => '']) . '@', $replace, $transliterated);
    }
    return new JsonResponse($transliterated);
  }
 /**
  * Tests the processOutbound() method with two parameter replacements.
  */
 public function testProcessOutboundDynamicTwo()
 {
     $this->csrfToken->expects($this->once())->method('get')->with('100/test-path/test')->will($this->returnValue('test_token'));
     $route = new Route('{slug_1}/test-path/{slug_2}', array(), array('_csrf_token' => 'TRUE'));
     $parameters = array('slug_1' => 100, 'slug_2' => 'test');
     $this->assertNull($this->processor->processOutbound('test', $route, $parameters));
 }
 /**
  * Tests the access() method with an invalid token.
  */
 public function testAccessTokenFail()
 {
     $this->csrfToken->expects($this->once())->method('validate')->with('test_query', 'test-path')->will($this->returnValue(FALSE));
     $this->routeMatch->expects($this->once())->method('getRawParameters')->will($this->returnValue(array()));
     $route = new Route('/test-path', array(), array('_csrf_token' => 'TRUE'));
     $request = Request::create('/test-path?token=test_query');
     $this->assertEquals(AccessResult::forbidden()->setCacheable(FALSE), $this->accessCheck->access($route, $request, $this->routeMatch));
 }
예제 #5
0
 /**
  * {@inheritdoc}
  */
 public function access(Route $route, Request $request, AccountInterface $account)
 {
     // Suppress notices when on other pages when menu system still checks access.
     $name = $request->attributes->get('name');
     $token = $request->query->get('token');
     $destination = $request->query->get('destination');
     return $account->hasPermission('switch users') && $this->csrfToken->validate($token, "devel/switch/{$name}|" . $destination, TRUE) ? static::ALLOW : static::DENY;
 }
예제 #6
0
 /**
  * {@inheritdoc}
  */
 public function submitForm(array &$form, FormStateInterface $form_state)
 {
     // We cannot rely on automatic token creation, since the csrf seed changes
     // after the redirect and the generated token is not more valid.
     // TODO find another way to do this.
     $url = Url::fromRoute('devel.switch', ['name' => $form_state->getValue('username')]);
     $url->setOption('query', ['token' => $this->csrfToken->get($url->getInternalPath())]);
     $form_state->setRedirectUrl($url);
 }
  protected function setUp() {
    parent::setUp();
    // Create the machine name controller.
    $this->tokenGenerator = $this->prophesize(CsrfTokenGenerator::class);
    $this->tokenGenerator->validate(Argument::cetera())->will(function ($args) {
      return $args[0] === 'token-' . $args[1];
    });

    $this->machineNameController = new MachineNameController(new PhpTransliteration(), $this->tokenGenerator->reveal());
  }
예제 #8
0
 /**
  * Tests the processOutbound() method with two parameter replacements.
  */
 public function testProcessOutboundDynamicTwo()
 {
     $this->csrfToken->expects($this->once())->method('get')->with('100/test-path/test')->will($this->returnValue('test_token'));
     $route = new Route('{slug_1}/test-path/{slug_2}', array(), array('_csrf_token' => 'TRUE'));
     $parameters = array('slug_1' => 100, 'slug_2' => 'test');
     $cacheable_metadata = new CacheableMetadata();
     $this->processor->processOutbound('test', $route, $parameters, $cacheable_metadata);
     // Cacheability of routes with a _csrf_token route requirement is max-age=0.
     $this->assertEquals((new CacheableMetadata())->setCacheMaxAge(0), $cacheable_metadata);
 }
예제 #9
0
 /**
  * Downloads a tarball of the site configuration.
  *
  * @param string $uri
  *   The URI to download.
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The request.
  *
  * @return \Symfony\Component\HttpFoundation\BinaryFileResponse
  *   The downloaded file.
  */
 public function downloadExport($uri, Request $request)
 {
     if ($uri) {
         // @todo Simplify once https://www.drupal.org/node/2630920 is solved.
         if (!$this->csrfToken->validate($request->query->get('token'), $uri)) {
             throw new AccessDeniedHttpException();
         }
         $request = new Request(array('file' => $uri));
         return $this->fileDownloadController->download($request, 'temporary');
     }
 }
예제 #10
0
 /**
  * {@inheritdoc}
  */
 public function processOutbound(Route $route, array &$parameters)
 {
     if ($route->hasRequirement('_csrf_token')) {
         $path = ltrim($route->getPath(), '/');
         // Replace the path parameters with values from the parameters array.
         foreach ($parameters as $param => $value) {
             $path = str_replace("{{$param}}", $value, $path);
         }
         // Adding this to the parameters means it will get merged into the query
         // string when the route is compiled.
         $parameters['token'] = $this->csrfToken->get($path);
     }
 }
예제 #11
0
 /**
  * Tests the processOutbound() method with no _csrf_token route requirement.
  */
 public function testProcessOutboundNoRequirement()
 {
     $this->csrfToken->expects($this->never())->method('get');
     $route = new Route('/test-path');
     $parameters = array();
     $bubbleable_metadata = new BubbleableMetadata();
     $this->processor->processOutbound('test', $route, $parameters, $bubbleable_metadata);
     // No parameters should be added to the parameters array.
     $this->assertEmpty($parameters);
     // Cacheability of routes without a _csrf_token route requirement is
     // unaffected.
     $this->assertEquals(new BubbleableMetadata(), $bubbleable_metadata);
 }
예제 #12
0
 /**
  * {@inheritdoc}
  */
 public function determineActiveTheme(RouteMatchInterface $route_match)
 {
     $ajax_page_state = $this->requestStack->getCurrentRequest()->request->get('ajax_page_state');
     $theme = $ajax_page_state['theme'];
     $token = $ajax_page_state['theme_token'];
     // Prevent a request forgery from giving a person access to a theme they
     // shouldn't be otherwise allowed to see. However, since everyone is
     // allowed to see the default theme, token validation isn't required for
     // that, and bypassing it allows most use-cases to work even when accessed
     // from the page cache.
     if ($theme === $this->configFactory->get('system.theme')->get('default') || $this->csrfGenerator->validate($token, $theme)) {
         return $theme;
     }
 }
예제 #13
0
 /**
  * Index.
  *
  * @return array
  *   Render array with all the entries.
  */
 public function index()
 {
     $output = ['#cache' => ['max-age' => 0]];
     // This is going to be reused at two places: once for the TableSortExtender,
     // and once for the table header itself.
     $header = [['data' => $this->t('Created'), 'field' => 'p.created'], ['data' => $this->t('Changed'), 'field' => 'p.changed'], ['data' => $this->t('Name'), 'field' => 'p.name'], ['data' => $this->t('Phone'), 'field' => 'p.phone'], ['data' => $this->t('Operations'), 'colspan' => '2']];
     $query = $this->connection->select('phonebook', 'p')->extend('Drupal\\Core\\Database\\Query\\TableSortExtender')->extend('Drupal\\Core\\Database\\Query\\PagerSelectExtender');
     $query->fields('p');
     $result = $query->orderByHeader($header)->limit(25)->execute();
     $output['table'] = ['#type' => 'table', '#header' => $header, '#empty' => $this->t('No entries found.')];
     foreach ($result as $row) {
         $output['table'][] = [['data' => ['#markup' => $this->date_formatter->format($row->created)]], ['data' => ['#markup' => $this->date_formatter->formatTimeDiffSince($row->changed)]], ['data' => ['#markup' => $row->name]], ['data' => ['#markup' => $row->phone]], ['data' => ['#markup' => $this->l($this->t('edit'), new Url('d8phonebook.edit', ['phonebook' => $row->pbid]))]], ['data' => ['#markup' => $this->l($this->t('delete'), new Url('d8phonebook.delete', ['phonebook' => $row->pbid], ['query' => ['token' => $this->csrf_token_generator->get('phonebook/' . $row->pbid . '/delete')]]))]]];
     }
     $output['pager'] = array('#type' => 'pager');
     return $output;
 }
예제 #14
0
  /**
   * @covers ::setCache
   */
  public function testSetCacheAuthUser() {
    $form_build_id = 'the_form_build_id';
    $form = [];
    $form_state = new FormState();

    $cache_token = 'the_cache_token';
    $form_data = $form;
    $form_data['#cache_token'] = $cache_token;
    $this->formCacheStore->expects($this->once())
      ->method('setWithExpire')
      ->with($form_build_id, $form_data, $this->isType('int'));

    $form_state_data = $form_state->getCacheableArray();
    $form_state_data['build_info']['safe_strings'] = [];
    $this->formStateCacheStore->expects($this->once())
      ->method('setWithExpire')
      ->with($form_build_id, $form_state_data, $this->isType('int'));

    $this->csrfToken->expects($this->once())
      ->method('get')
      ->willReturn($cache_token);
    $this->account->expects($this->once())
      ->method('isAuthenticated')
      ->willReturn(TRUE);

    $this->formCache->setCache($form_build_id, $form, $form_state);
  }
예제 #15
0
 /**
  * {@inheritdoc}
  */
 public function setCache($form_build_id, $form, FormStateInterface $form_state)
 {
     // 6 hours cache life time for forms should be plenty.
     $expire = 21600;
     // Ensure that the form build_id embedded in the form structure is the same
     // as the one passed in as a parameter. This is an additional safety measure
     // to prevent legacy code operating directly with
     // \Drupal::formBuilder()->getCache() and \Drupal::formBuilder()->setCache()
     // from accidentally overwriting immutable form state.
     if (isset($form['#build_id']) && $form['#build_id'] != $form_build_id) {
         $this->logger->error('Form build-id mismatch detected while attempting to store a form in the cache.');
         return;
     }
     // Cache form structure.
     if (isset($form)) {
         if ($this->currentUser->isAuthenticated()) {
             $form['#cache_token'] = $this->csrfToken->get();
         }
         unset($form['#build_id_old']);
         $this->keyValueExpirableFactory->get('form')->setWithExpire($form_build_id, $form, $expire);
     }
     if ($data = $form_state->getCacheableArray()) {
         $this->keyValueExpirableFactory->get('form_state')->setWithExpire($form_build_id, $data, $expire);
     }
 }
예제 #16
0
 /**
  * Tests the access() method with no _controller_request attribute set.
  *
  * This will use the 'ALL' access conjunction.
  */
 public function testAccessTokenMissAll()
 {
     $this->csrfToken->expects($this->never())->method('validate');
     $route = new Route('/test-path', array(), array('_csrf_token' => 'TRUE'), array('_access_mode' => 'ALL'));
     $request = new Request(array('token' => 'test_query'));
     $this->assertSame(AccessInterface::ALLOW, $this->accessCheck->access($route, $request, $this->account));
 }
예제 #17
0
 /**
  * {@inheritdoc}
  */
 public function setCache($form_build_id, $form, FormStateInterface $form_state)
 {
     // 6 hours cache life time for forms should be plenty.
     $expire = 21600;
     // Ensure that the form build_id embedded in the form structure is the same
     // as the one passed in as a parameter. This is an additional safety measure
     // to prevent legacy code operating directly with
     // \Drupal::formBuilder()->getCache() and \Drupal::formBuilder()->setCache()
     // from accidentally overwriting immutable form state.
     if (isset($form['#build_id']) && $form['#build_id'] != $form_build_id) {
         $this->logger->error('Form build-id mismatch detected while attempting to store a form in the cache.');
         return;
     }
     // Cache form structure.
     if (isset($form)) {
         if ($this->currentUser->isAuthenticated()) {
             $form['#cache_token'] = $this->csrfToken->get();
         }
         unset($form['#build_id_old']);
         $this->keyValueExpirableFactory->get('form')->setWithExpire($form_build_id, $form, $expire);
     }
     // Cache form state.
     if ($this->configFactory->get('system.performance')->get('cache.page.use_internal') && $this->isPageCacheable()) {
         $form_state->addBuildInfo('immutable', TRUE);
     }
     // Store the known list of safe strings for form re-use.
     // @todo Ensure we are not storing an excessively large string list in:
     //   https://www.drupal.org/node/2295823
     $form_state->addBuildInfo('safe_strings', SafeMarkup::getAll());
     if ($data = $form_state->getCacheableArray()) {
         $this->keyValueExpirableFactory->get('form_state')->setWithExpire($form_build_id, $data, $expire);
     }
 }
 /**
  * Checks access based on a CSRF token for the request.
  *
  * @param \Symfony\Component\Routing\Route $route
  *   The route to check against.
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The request object.
  * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
  *   The route match object.
  *
  * @return \Drupal\Core\Access\AccessResultInterface
  *   The access result.
  */
 public function access(Route $route, Request $request, RouteMatchInterface $route_match)
 {
     $parameters = $route_match->getRawParameters();
     $path = ltrim($route->getPath(), '/');
     // Replace the path parameters with values from the parameters array.
     foreach ($parameters as $param => $value) {
         $path = str_replace("{{$param}}", $value, $path);
     }
     if ($this->csrfToken->validate($request->query->get('token'), $path)) {
         $result = AccessResult::allowed();
     } else {
         $result = AccessResult::forbidden();
     }
     // Not cacheable because the CSRF token is highly dynamic.
     return $result->setCacheable(FALSE);
 }
예제 #19
0
 /**
  * {@inheritdoc}
  */
 public function validateForm($form_id, &$form, FormStateInterface &$form_state)
 {
     // If this form is flagged to always validate, ensure that previous runs of
     // validation are ignored.
     if ($form_state->isValidationEnforced()) {
         $form_state->setValidationComplete(FALSE);
     }
     // If this form has completed validation, do not validate again.
     if ($form_state->isValidationComplete()) {
         return;
     }
     // If the session token was set by self::prepareForm(), ensure that it
     // matches the current user's session. This is duplicate to code in
     // FormBuilder::doBuildForm() but left to protect any custom form handling
     // code.
     if (isset($form['#token'])) {
         if (!$this->csrfToken->validate($form_state->getValue('form_token'), $form['#token']) || $form_state->hasInvalidToken()) {
             $this->setInvalidTokenError($form_state);
             // Stop here and don't run any further validation handlers, because they
             // could invoke non-safe operations which opens the door for CSRF
             // vulnerabilities.
             $this->finalizeValidation($form, $form_state, $form_id);
             return;
         }
     }
     // Recursively validate each form element.
     $this->doValidateForm($form, $form_state, $form_id);
     $this->finalizeValidation($form, $form_state, $form_id);
     $this->handleErrorsWithLimitedValidation($form, $form_state, $form_id);
 }
예제 #20
0
 /**
  * {@inheritdoc}
  */
 public function processOutbound($route_name, Route $route, array &$parameters, CacheableMetadata $cacheable_metadata = NULL)
 {
     if ($route->hasRequirement('_csrf_token')) {
         $path = ltrim($route->getPath(), '/');
         // Replace the path parameters with values from the parameters array.
         foreach ($parameters as $param => $value) {
             $path = str_replace("{{$param}}", $value, $path);
         }
         // Adding this to the parameters means it will get merged into the query
         // string when the route is compiled.
         $parameters['token'] = $this->csrfToken->get($path);
         if ($cacheable_metadata) {
             // Tokens are per user and per session, so not cacheable.
             // @todo Improve in https://www.drupal.org/node/2351015.
             $cacheable_metadata->setCacheMaxAge(0);
         }
     }
 }
예제 #21
0
 /**
  * Checks access based on a CSRF token for the request.
  *
  * @param \Symfony\Component\Routing\Route $route
  *   The route to check against.
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The request object.
  *
  * @return string
  *   A \Drupal\Core\Access\AccessInterface constant value.
  */
 public function access(Route $route, Request $request)
 {
     // If this is the controller request, check CSRF access as normal.
     if ($request->attributes->get('_controller_request')) {
         // @todo Remove dependency on the internal _system_path attribute:
         //   https://www.drupal.org/node/2293501.
         return $this->csrfToken->validate($request->query->get('token'), $request->attributes->get('_system_path')) ? static::ALLOW : static::KILL;
     }
     // Otherwise, this could be another requested access check that we don't
     // want to check CSRF tokens on.
     $conjunction = $route->getOption('_access_mode') ?: 'ANY';
     // Return ALLOW if all access checks are needed.
     if ($conjunction == 'ALL') {
         return static::ALLOW;
     } else {
         return static::DENY;
     }
 }
 /**
  * @covers ::determineActiveTheme
  */
 public function testDetermineActiveThemeDefaultTheme()
 {
     $theme = 'bartik';
     // When the theme is the system default, an empty string is provided as the
     // theme token. See system_js_settings_alter().
     $theme_token = '';
     $request = new Request([], ['ajax_page_state' => ['theme' => $theme, 'theme_token' => $theme_token]]);
     $this->requestStack->push($request);
     $route_match = RouteMatch::createFromRequest($request);
     $this->tokenGenerator->validate(Argument::cetera())->shouldNotBeCalled();
     $result = $this->negotiator->determineActiveTheme($route_match);
     $this->assertSame($theme, $result);
 }
예제 #23
0
 /**
  * @covers ::validateForm
  */
 public function testValidateValidFormToken()
 {
     $request_stack = new RequestStack();
     $this->csrfToken->expects($this->once())->method('validate')->will($this->returnValue(TRUE));
     $form_validator = $this->getMockBuilder('Drupal\\Core\\Form\\FormValidator')->setConstructorArgs([$request_stack, $this->getStringTranslationStub(), $this->csrfToken, $this->logger, $this->formErrorHandler])->setMethods(array('doValidateForm'))->getMock();
     $form_validator->expects($this->once())->method('doValidateForm');
     $form['#token'] = 'test_form_id';
     $form_state = $this->getMockBuilder('Drupal\\Core\\Form\\FormState')->setMethods(array('setErrorByName'))->getMock();
     $form_state->expects($this->never())->method('setErrorByName');
     $form_state->setValue('form_token', 'some_random_token');
     $form_validator->validateForm('test_form_id', $form, $form_state);
     $this->assertTrue($form_state->isValidationComplete());
 }
 /**
  * Logs in a user.
  *
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The request.
  *
  * @return \Symfony\Component\HttpFoundation\Response
  *   A response which contains the ID and CSRF token.
  */
 public function login(Request $request)
 {
     $format = $this->getRequestFormat($request);
     $content = $request->getContent();
     $credentials = $this->serializer->decode($content, $format);
     if (!isset($credentials['name']) && !isset($credentials['pass'])) {
         throw new BadRequestHttpException('Missing credentials.');
     }
     if (!isset($credentials['name'])) {
         throw new BadRequestHttpException('Missing credentials.name.');
     }
     if (!isset($credentials['pass'])) {
         throw new BadRequestHttpException('Missing credentials.pass.');
     }
     $this->floodControl($request, $credentials['name']);
     if ($this->userIsBlocked($credentials['name'])) {
         throw new BadRequestHttpException('The user has not been activated or is blocked.');
     }
     if ($uid = $this->userAuth->authenticate($credentials['name'], $credentials['pass'])) {
         $this->flood->clear('user.http_login', $this->getLoginFloodIdentifier($request, $credentials['name']));
         /** @var \Drupal\user\UserInterface $user */
         $user = $this->userStorage->load($uid);
         $this->userLoginFinalize($user);
         // Send basic metadata about the logged in user.
         $response_data = [];
         if ($user->get('uid')->access('view', $user)) {
             $response_data['current_user']['uid'] = $user->id();
         }
         if ($user->get('roles')->access('view', $user)) {
             $response_data['current_user']['roles'] = $user->getRoles();
         }
         if ($user->get('name')->access('view', $user)) {
             $response_data['current_user']['name'] = $user->getAccountName();
         }
         $response_data['csrf_token'] = $this->csrfToken->get('rest');
         $logout_route = $this->routeProvider->getRouteByName('user.logout.http');
         // Trim '/' off path to match \Drupal\Core\Access\CsrfAccessCheck.
         $logout_path = ltrim($logout_route->getPath(), '/');
         $response_data['logout_token'] = $this->csrfToken->get($logout_path);
         $encoded_response_data = $this->serializer->encode($response_data, $format);
         return new Response($encoded_response_data);
     }
     $flood_config = $this->config('user.flood');
     if ($identifier = $this->getLoginFloodIdentifier($request, $credentials['name'])) {
         $this->flood->register('user.http_login', $flood_config->get('user_window'), $identifier);
     }
     // Always register an IP-based failed login event.
     $this->flood->register('user.failed_login_ip', $flood_config->get('ip_window'));
     throw new BadRequestHttpException('Sorry, unrecognized username or password.');
 }
 /**
  * Checks access.
  *
  * @param \Symfony\Component\HttpFoundation\Request $request
  *   The request object.
  * @param \Drupal\Core\Session\AccountInterface $account
  *   The currently logged in account.
  *
  * @return \Drupal\Core\Access\AccessResultInterface
  *   The access result.
  */
 public function access(Request $request, AccountInterface $account)
 {
     $method = $request->getMethod();
     // This check only applies if
     // 1. this is a write operation
     // 2. the user was successfully authenticated and
     // 3. the request comes with a session cookie.
     if (!in_array($method, array('GET', 'HEAD', 'OPTIONS', 'TRACE')) && $account->isAuthenticated() && $this->sessionConfiguration->hasSession($request)) {
         $csrf_token = $request->headers->get('X-CSRF-Token');
         // @todo Remove validate call using 'rest' in 8.3.
         //   Kept here for sessions active during update.
         if (!$this->csrfToken->validate($csrf_token, self::TOKEN_KEY) && !$this->csrfToken->validate($csrf_token, 'rest')) {
             return AccessResult::forbidden()->setReason('X-CSRF-Token request header is missing')->setCacheMaxAge(0);
         }
     }
     // Let other access checkers decide if the request is legit.
     return AccessResult::allowed()->setCacheMaxAge(0);
 }
 /**
  * {@inheritdoc}
  */
 public function exportFormSubmit(array &$form, FormStateInterface $form_state)
 {
     // Redirect to the archive file download.
     $form_state->setRedirect('features.export_download', ['uri' => $this->archiveName, 'token' => $this->csrfToken->get($this->archiveName)]);
 }
예제 #27
0
 /**
  * {@inheritdoc}
  */
 public function create(array $batch)
 {
     // Ensure that a session is started before using the CSRF token generator.
     $this->session->start();
     $this->connection->insert('batch')->fields(array('bid' => $batch['id'], 'timestamp' => REQUEST_TIME, 'token' => $this->csrfToken->get($batch['id']), 'batch' => serialize($batch)))->execute();
 }
예제 #28
0
 /**
  * #lazy_builder callback; gets a CSRF token for the given path.
  *
  * @param string $path
  *   The path to get a CSRF token for.
  *
  * @return array
  *   A renderable array representing the CSRF token.
  */
 public function renderPlaceholderCsrfToken($path)
 {
     return ['#markup' => $this->csrfToken->get($path), '#cache' => ['contexts' => ['session']]];
 }
 /**
  * Tests the exception thrown when no 'hash_salt' is provided in settings.
  *
  * @covers ::get
  * @expectedException \RuntimeException
  */
 public function testGetWithNoHashSalt()
 {
     // Update settings with no hash salt.
     new Settings(array());
     $generator = new CsrfTokenGenerator($this->privateKey, $this->sessionMetadata);
     $generator->get();
 }
예제 #30
0
 /**
  * {@inheritdoc}
  */
 public function doBuildForm($form_id, &$element, FormStateInterface &$form_state)
 {
     // Initialize as unprocessed.
     $element['#processed'] = FALSE;
     // Use element defaults.
     if (isset($element['#type']) && empty($element['#defaults_loaded']) && ($info = $this->elementInfo->getInfo($element['#type']))) {
         // Overlay $info onto $element, retaining preexisting keys in $element.
         $element += $info;
         $element['#defaults_loaded'] = TRUE;
     }
     // Assign basic defaults common for all form elements.
     $element += array('#required' => FALSE, '#attributes' => array(), '#title_display' => 'before', '#description_display' => 'after', '#errors' => NULL);
     // Special handling if we're on the top level form element.
     if (isset($element['#type']) && $element['#type'] == 'form') {
         if (!empty($element['#https']) && !UrlHelper::isExternal($element['#action'])) {
             global $base_root;
             // Not an external URL so ensure that it is secure.
             $element['#action'] = str_replace('http://', 'https://', $base_root) . $element['#action'];
         }
         // Store a reference to the complete form in $form_state prior to building
         // the form. This allows advanced #process and #after_build callbacks to
         // perform changes elsewhere in the form.
         $form_state->setCompleteForm($element);
         // Set a flag if we have a correct form submission. This is always TRUE
         // for programmed forms coming from self::submitForm(), or if the form_id
         // coming from the POST data is set and matches the current form_id.
         $input = $form_state->getUserInput();
         if ($form_state->isProgrammed() || !empty($input) && (isset($input['form_id']) && $input['form_id'] == $form_id)) {
             $form_state->setProcessInput();
             if (isset($element['#token'])) {
                 $input = $form_state->getUserInput();
                 if (empty($input['form_token']) || !$this->csrfToken->validate($input['form_token'], $element['#token'])) {
                     // Set an early form error to block certain input processing since
                     // that opens the door for CSRF vulnerabilities.
                     $this->setInvalidTokenError($form_state);
                     // This value is checked in self::handleInputElement().
                     $form_state->setInvalidToken(TRUE);
                     // Make sure file uploads do not get processed.
                     $this->requestStack->getCurrentRequest()->files = new FileBag();
                 }
             }
         } else {
             $form_state->setProcessInput(FALSE);
         }
         // All form elements should have an #array_parents property.
         $element['#array_parents'] = array();
     }
     if (!isset($element['#id'])) {
         $unprocessed_id = 'edit-' . implode('-', $element['#parents']);
         $element['#id'] = Html::getUniqueId($unprocessed_id);
         // Provide a selector usable by JavaScript. As the ID is unique, its not
         // possible to rely on it in JavaScript.
         $element['#attributes']['data-drupal-selector'] = Html::getId($unprocessed_id);
     } else {
         // Provide a selector usable by JavaScript. As the ID is unique, its not
         // possible to rely on it in JavaScript.
         $element['#attributes']['data-drupal-selector'] = Html::getId($element['#id']);
     }
     // Add the aria-describedby attribute to associate the form control with its
     // description.
     if (!empty($element['#description'])) {
         $element['#attributes']['aria-describedby'] = $element['#id'] . '--description';
     }
     // Handle input elements.
     if (!empty($element['#input'])) {
         $this->handleInputElement($form_id, $element, $form_state);
     }
     // Allow for elements to expand to multiple elements, e.g., radios,
     // checkboxes and files.
     if (isset($element['#process']) && !$element['#processed']) {
         foreach ($element['#process'] as $callback) {
             $complete_form =& $form_state->getCompleteForm();
             $element = call_user_func_array($form_state->prepareCallback($callback), array(&$element, &$form_state, &$complete_form));
         }
         $element['#processed'] = TRUE;
     }
     // We start off assuming all form elements are in the correct order.
     $element['#sorted'] = TRUE;
     // Recurse through all child elements.
     $count = 0;
     if (isset($element['#access'])) {
         $access = $element['#access'];
         $inherited_access = NULL;
         if ($access instanceof AccessResultInterface && !$access->isAllowed() || $access === FALSE) {
             $inherited_access = $access;
         }
     }
     foreach (Element::children($element) as $key) {
         // Prior to checking properties of child elements, their default
         // properties need to be loaded.
         if (isset($element[$key]['#type']) && empty($element[$key]['#defaults_loaded']) && ($info = $this->elementInfo->getInfo($element[$key]['#type']))) {
             $element[$key] += $info;
             $element[$key]['#defaults_loaded'] = TRUE;
         }
         // Don't squash an existing tree value.
         if (!isset($element[$key]['#tree'])) {
             $element[$key]['#tree'] = $element['#tree'];
         }
         // Children inherit #access from parent.
         if (isset($inherited_access)) {
             $element[$key]['#access'] = $inherited_access;
         }
         // Make child elements inherit their parent's #disabled and #allow_focus
         // values unless they specify their own.
         foreach (array('#disabled', '#allow_focus') as $property) {
             if (isset($element[$property]) && !isset($element[$key][$property])) {
                 $element[$key][$property] = $element[$property];
             }
         }
         // Don't squash existing parents value.
         if (!isset($element[$key]['#parents'])) {
             // Check to see if a tree of child elements is present. If so,
             // continue down the tree if required.
             $element[$key]['#parents'] = $element[$key]['#tree'] && $element['#tree'] ? array_merge($element['#parents'], array($key)) : array($key);
         }
         // Ensure #array_parents follows the actual form structure.
         $array_parents = $element['#array_parents'];
         $array_parents[] = $key;
         $element[$key]['#array_parents'] = $array_parents;
         // Assign a decimal placeholder weight to preserve original array order.
         if (!isset($element[$key]['#weight'])) {
             $element[$key]['#weight'] = $count / 1000;
         } else {
             // If one of the child elements has a weight then we will need to sort
             // later.
             unset($element['#sorted']);
         }
         $element[$key] = $this->doBuildForm($form_id, $element[$key], $form_state);
         $count++;
     }
     // The #after_build flag allows any piece of a form to be altered
     // after normal input parsing has been completed.
     if (isset($element['#after_build']) && !isset($element['#after_build_done'])) {
         foreach ($element['#after_build'] as $callback) {
             $element = call_user_func_array($form_state->prepareCallback($callback), array($element, &$form_state));
         }
         $element['#after_build_done'] = TRUE;
     }
     // If there is a file element, we need to flip a flag so later the
     // form encoding can be set.
     if (isset($element['#type']) && $element['#type'] == 'file') {
         $form_state->setHasFileElement();
     }
     // Final tasks for the form element after self::doBuildForm() has run for
     // all other elements.
     if (isset($element['#type']) && $element['#type'] == 'form') {
         // If there is a file element, we set the form encoding.
         if ($form_state->hasFileElement()) {
             $element['#attributes']['enctype'] = 'multipart/form-data';
         }
         // Allow Ajax submissions to the form action to bypass verification. This
         // is especially useful for multipart forms, which cannot be verified via
         // a response header.
         $element['#attached']['drupalSettings']['ajaxTrustedUrl'][$element['#action']] = TRUE;
         // If a form contains a single textfield, and the ENTER key is pressed
         // within it, Internet Explorer submits the form with no POST data
         // identifying any submit button. Other browsers submit POST data as
         // though the user clicked the first button. Therefore, to be as
         // consistent as we can be across browsers, if no 'triggering_element' has
         // been identified yet, default it to the first button.
         $buttons = $form_state->getButtons();
         if (!$form_state->isProgrammed() && !$form_state->getTriggeringElement() && !empty($buttons)) {
             $form_state->setTriggeringElement($buttons[0]);
         }
         $triggering_element = $form_state->getTriggeringElement();
         // If the triggering element specifies "button-level" validation and
         // submit handlers to run instead of the default form-level ones, then add
         // those to the form state.
         if (isset($triggering_element['#validate'])) {
             $form_state->setValidateHandlers($triggering_element['#validate']);
         }
         if (isset($triggering_element['#submit'])) {
             $form_state->setSubmitHandlers($triggering_element['#submit']);
         }
         // If the triggering element executes submit handlers, then set the form
         // state key that's needed for those handlers to run.
         if (!empty($triggering_element['#executes_submit_callback'])) {
             $form_state->setSubmitted();
         }
         // Special processing if the triggering element is a button.
         if (!empty($triggering_element['#is_button'])) {
             // Because there are several ways in which the triggering element could
             // have been determined (including from input variables set by
             // JavaScript or fallback behavior implemented for IE), and because
             // buttons often have their #name property not derived from their
             // #parents property, we can't assume that input processing that's
             // happened up until here has resulted in
             // $form_state->getValue(BUTTON_NAME) being set. But it's common for
             // forms to have several buttons named 'op' and switch on
             // $form_state->getValue('op') during submit handler execution.
             $form_state->setValue($triggering_element['#name'], $triggering_element['#value']);
         }
     }
     return $element;
 }