/** * {@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); }
/** * {@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); } }
/** * {@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); } }
/** * 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; }
/** * {@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); } }
/** * {@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); } } }
/** * 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.'); }
/** * #lazy_builder callback; renders form CSRF token. * * @param string $placeholder * A string containing a placeholder, matching the value of the form's * #token. * * @return array * A renderable array containing the CSRF token. */ public function renderFormTokenPlaceholder($placeholder) { return ['#markup' => $this->csrfToken->get($placeholder), '#cache' => ['contexts' => ['session']]]; }
/** * {@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)]); }
/** * {@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(); }
/** * #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(); }
/** * {@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); }
/** * {@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); }
/** * Returns a CSRF protecting session token. * * @return \Symfony\Component\HttpFoundation\Response * The response object. */ public function csrfToken() { return new Response($this->tokenGenerator->get(CsrfRequestHeaderAccessCheck::TOKEN_KEY), 200, ['Content-Type' => 'text/plain']); }
/** * Returns a CSRF using the deprecated 'rest' value protecting session token. * * @return \Symfony\Component\HttpFoundation\Response * The response object. */ public function csrfToken() { return new Response($this->tokenGenerator->get('rest'), 200, ['Content-Type' => 'text/plain']); }
/** * {@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); }
/** * Saves a batch. * * @param array $batch * The array representing the batch to create. */ protected function doCreate(array $batch) { $this->connection->insert('batch')->fields(array('bid' => $batch['id'], 'timestamp' => REQUEST_TIME, 'token' => $this->csrfToken->get($batch['id']), 'batch' => serialize($batch)))->execute(); }
/** * Provides the Switch user list. */ public function switchUserList() { $list_size = $this->configuration['list_size']; $include_anon = $this->configuration['include_anon']; $anon = new AnonymousUserSession(); $links = array(); if ($this->currentUser->hasPermission('switch users')) { if ($include_anon) { --$list_size; } $dest = $this->redirectDestination->getAsArray(); // Try to find at least $list_size users that can switch. // Inactive users are omitted from all of the following db selects. $roles = user_roles(TRUE, 'switch users'); $query = db_select('users', 'u'); $query->join('users_field_data', 'ufd'); $query->addField('u', 'uid'); $query->addField('ufd', 'access'); $query->distinct(); $query->condition('u.uid', 0, '>'); $query->condition('ufd.status', 0, '>'); $query->orderBy('ufd.access', 'DESC'); $query->range(0, $list_size); if (!isset($roles[DRUPAL_AUTHENTICATED_RID])) { $query->leftJoin('users_roles', 'r', 'u.uid = r.uid'); $or_condition = db_or(); $or_condition->condition('u.uid', 1); if (!empty($roles)) { $or_condition->condition('r.rid', array_keys($roles), 'IN'); } $query->condition($or_condition); } $uids = $query->execute()->fetchCol(); $accounts = user_load_multiple($uids); foreach ($accounts as $account) { $path = 'devel/switch/' . $account->name->value; $links[$account->id()] = array('title' => user_format_name($account), 'href' => $path, 'query' => $dest + array('token' => $this->csrfTokenGenerator->get($path)), 'attributes' => array('title' => t('This user can switch back.')), 'html' => TRUE, 'last_access' => $account->access->value); } $num_links = count($links); if ($num_links < $list_size) { // If we don't have enough, add distinct uids until we hit $list_size. $uids = db_query_range('SELECT u.uid FROM {users} u INNER JOIN {users_field_data} ufd WHERE u.uid > 0 AND u.uid NOT IN (:uids) AND ufd.status > 0 ORDER BY ufd.access DESC', 0, $list_size - $num_links, array(':uids' => array_keys($links)))->fetchCol(); $accounts = user_load_multiple($uids); foreach ($accounts as $account) { $path = 'devel/switch/' . $account->name->value; $links[$account->id()] = array('title' => user_format_name($account), 'href' => $path, 'query' => $dest + array('token' => $this->csrfTokenGenerator->get($path)), 'attributes' => array('title' => t('Caution: this user will be unable to switch back.')), 'last_access' => $account->access->value); } uasort($links, '_devel_switch_user_list_cmp'); } if ($include_anon) { $path = 'devel/switch'; $link = array('title' => $anon->getUsername(), 'href' => $path, 'query' => $dest + array('token' => $this->csrfTokenGenerator->get($path)), 'attributes' => array('title' => t('Caution: the anonymous user will be unable to switch back.'))); if ($this->currentUser->hasPermission('switch users')) { $link['title'] = SafeMarkup::placeholder($link['title']); $link['attributes'] = array('title' => t('This user can switch back.')); $link['html'] = TRUE; } $links[$anon->id()] = $link; } } if (array_key_exists($uid = $this->currentUser->id(), $links)) { $links[$uid]['title'] = '<strong>' . $links[$uid]['title'] . '</strong>'; } return $links; }
/** * {@inheritdoc} */ public function submitForm(array &$form, FormStateInterface $form_state) { $name = $form_state->getValue('username'); $path = 'devel/switch/' . $name; $form_state->setRedirect('devel.switch', array('name' => $name), array('query' => array('destination' => '', 'token' => $this->csrfTokenGenerator->get($path)))); }